Decompiled source of Stride v0.7.1

BepInEx/plugins/Stride.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using ServerSync;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Stride")]
[assembly: AssemblyDescription("Invisible improvements to vanilla movement in Valheim")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Stride")]
[assembly: AssemblyCopyright("Copyright (c) 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("77f1130e-4e67-4f2d-bd7e-d365e48f9120")]
[assembly: AssemblyFileVersion("0.7.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.7.1.0")]
namespace StrideMod
{
	internal static class PluginInfo
	{
		internal const string Guid = "com.b0n3.stride";

		internal const string Name = "Stride";

		internal const string Version = "0.7.1";
	}
	[BepInPlugin("com.b0n3.stride", "Stride", "0.7.1")]
	public class StridePlugin : BaseUnityPlugin
	{
		internal enum DebugLogFeature
		{
			CoyoteTime,
			JumpBuffering,
			WallJump,
			FallRoll,
			StaminaDebt
		}

		private sealed class FloatSetting
		{
			private readonly ConfigEntry<string> entry;

			private readonly float defaultValue;

			private readonly float minValue;

			private readonly float maxValue;

			internal float Value => GetFloat(entry, defaultValue, minValue, maxValue);

			internal FloatSetting(ConfigEntry<string> entry, float defaultValue, float minValue, float maxValue)
			{
				this.entry = entry;
				this.defaultValue = defaultValue;
				this.minValue = minValue;
				this.maxValue = maxValue;
			}
		}

		private const string LegacyConfigFileName = "com.b0n3.walljump.cfg";

		private const string CoyoteTimeSection = "Coyote Time";

		private const string JumpBufferingSection = "Jump Buffering";

		private const string WallJumpSection = "Wall Jump";

		private const string FallRollSection = "Fall Roll";

		private const string StaminaDebtSection = "Stamina Debt";

		private const string LoggingSection = "Logging";

		internal static ManualLogSource Log;

		internal static readonly ConfigSync ServerConfig = new ConfigSync("com.b0n3.stride")
		{
			DisplayName = "Stride",
			CurrentVersion = "0.7.1",
			MinimumRequiredVersion = "0.7.0",
			ModRequired = false
		};

		private static readonly Dictionary<string, string> LastInvalidConfigValues = new Dictionary<string, string>();

		private Dictionary<string, string> existingConfigValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		private static FloatSetting coyoteTimeSeconds;

		private static FloatSetting jumpBufferSeconds;

		private static FloatSetting jumpWindowSeconds;

		private static FloatSetting wallMemorySeconds;

		private static FloatSetting wallCheckDistance;

		private static FloatSetting wallCheckRadius;

		private static FloatSetting wallJumpTriggerDistance;

		private static FloatSetting minimumWallJumpEntrySpeed;

		private static FloatSetting fallRollBufferSeconds;

		private static FloatSetting fallRollDamageReduction;

		private static FloatSetting horizontalBounceMinSpeed;

		private static FloatSetting outwardBounceMinSpeed;

		private static FloatSetting reflectionResponsiveness;

		private static FloatSetting inputBiasFactor;

		private static FloatSetting verticalPushForce;

		private static FloatSetting cooldownSeconds;

		internal static ConfigEntry<bool> CoyoteTimeEnabled;

		internal static ConfigEntry<bool> JumpBufferingEnabled;

		internal static ConfigEntry<bool> WallJumpEnabled;

		internal static ConfigEntry<bool> FallRollEnabled;

		internal static ConfigEntry<bool> StaminaDebtEnabled;

		internal static ConfigEntry<bool> RequireForwardPress;

		internal static ConfigEntry<bool> EnableVerboseLogging;

		internal static ConfigEntry<bool> VerboseAllFeatures;

		internal static ConfigEntry<bool> CoyoteTimeVerboseLogging;

		internal static ConfigEntry<bool> JumpBufferVerboseLogging;

		internal static ConfigEntry<bool> WallJumpVerboseLogging;

		internal static ConfigEntry<bool> FallRollVerboseLogging;

		internal static ConfigEntry<bool> StaminaDebtVerboseLogging;

		internal static float CoyoteTimeSeconds => coyoteTimeSeconds.Value;

		internal static float JumpBufferSeconds => jumpBufferSeconds.Value;

		internal static float JumpWindowSeconds => jumpWindowSeconds.Value;

		internal static float WallMemorySeconds => wallMemorySeconds.Value;

		internal static float WallCheckDistance => wallCheckDistance.Value;

		internal static float WallCheckRadius => wallCheckRadius.Value;

		internal static float WallJumpTriggerDistance => wallJumpTriggerDistance.Value;

		internal static float MinimumWallJumpEntrySpeed => minimumWallJumpEntrySpeed.Value;

		internal static float FallRollBufferSeconds => fallRollBufferSeconds.Value;

		internal static float FallRollDamageReduction => fallRollDamageReduction.Value;

		internal static float HorizontalBounceMinSpeed => horizontalBounceMinSpeed.Value;

		internal static float OutwardBounceMinSpeed => outwardBounceMinSpeed.Value;

		internal static float ReflectionResponsiveness => reflectionResponsiveness.Value;

		internal static float InputBiasFactor => inputBiasFactor.Value;

		internal static float VerticalPushForce => verticalPushForce.Value;

		internal static float CooldownSeconds => cooldownSeconds.Value;

		private void Awake()
		{
			//IL_0624: Unknown result type (might be due to invalid IL or missing references)
			//IL_062a: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			EnsureLegacyConfigMigrated();
			existingConfigValues = ReadConfigValues(((BaseUnityPlugin)this).Config.ConfigFilePath);
			CoyoteTimeEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Coyote Time", "Enabled", true, "If this is off, Stride does not allow late jumps after leaving a ledge.");
			coyoteTimeSeconds = BindFloatSetting("Coyote Time", "WindowSeconds", 0.1f, 0f, 1f, "How long you can still jump after running off a ledge.", "General", "CoyoteTimeSeconds");
			JumpBufferingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Jump Buffering", "Enabled", true, "If this is off, Stride does not save early jump presses before landing.");
			jumpBufferSeconds = BindFloatSetting("Jump Buffering", "WindowSeconds", 0.1f, 0f, 1f, "How early a jump button press is saved before you land.", "General", "JumpBufferSeconds");
			WallJumpEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Wall Jump", "Enabled", true, "If this is off, Stride does not allow wall jumps.");
			jumpWindowSeconds = BindFloatSetting("Wall Jump", "AirJumpWindowSeconds", 0.95f, 0f, 5f, "How long after a normal jump you are still allowed to trigger a wall jump.", "General", "JumpWindowSeconds");
			wallMemorySeconds = BindFloatSetting("Wall Jump", "WallMemorySeconds", 0.05f, 0f, 1f, "How long a wall stays valid after you stop touching it.", "General", "WallMemorySeconds");
			wallCheckDistance = BindFloatSetting("Wall Jump", "WallCheckDistance", 0.575f, 0.05f, 5f, "How far away Stride looks for a wall you can jump from.", "General", "WallCheckDistance");
			wallCheckRadius = BindFloatSetting("Wall Jump", "WallCheckRadius", 0.225f, 0.05f, 3f, "How wide the wall search is around your character.", "General", "WallCheckRadius");
			wallJumpTriggerDistance = BindFloatSetting("Wall Jump", "WallJumpTriggerDistance", 0.18f, 0.01f, 2f, "How close the chosen wall must be before the wall jump actually fires.", "General", "WallJumpTriggerDistance");
			minimumWallJumpEntrySpeed = BindFloatSetting("Wall Jump", "MinimumEntrySpeed", 0.1f, 0f, 20f, "The minimum sideways movement speed that counts as a real wall approach.", "General", "MinimumWallJumpEntrySpeed");
			horizontalBounceMinSpeed = BindFloatSetting("Wall Jump", "MinimumHorizontalBounceSpeed", 9.5f, 0f, 50f, "The minimum sideways speed you keep after bouncing off a wall.", "General", "HorizontalBounceMinSpeed");
			outwardBounceMinSpeed = BindFloatSetting("Wall Jump", "MinimumOutwardBounceSpeed", 6.75f, 0f, 50f, "The minimum speed that pushes you away from the wall.", "General", "OutwardBounceMinSpeed");
			reflectionResponsiveness = BindFloatSetting("Wall Jump", "ReflectionResponsiveness", 1f, 0f, 3f, "How much of your incoming wall approach speed carries into the bounce.", "General", "ReflectionResponsiveness");
			inputBiasFactor = BindFloatSetting("Wall Jump", "InputBiasFactor", 0.12f, 0f, 1f, "How much your movement input can gently steer the wall jump along the wall.", "General", "InputBiasFactor");
			verticalPushForce = BindFloatSetting("Wall Jump", "VerticalPushForce", 8.6f, 0f, 50f, "How much upward lift the wall jump gives you.", "General", "VerticalPushForce");
			cooldownSeconds = BindFloatSetting("Wall Jump", "CooldownSeconds", 0.15f, 0f, 10f, "How long Stride waits before another wall jump can happen.", "General", "CooldownSeconds");
			FallRollEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Fall Roll", "Enabled", true, "If this is off, Stride does not buffer fall rolls or reduce fall damage on landing.");
			fallRollBufferSeconds = BindFloatSetting("Fall Roll", "BufferSeconds", 0.9f, 0f, 2f, "How early you can press dodge before landing and still get a fall roll.", "General", "FallRollBufferSeconds");
			fallRollDamageReduction = BindFloatSetting("Fall Roll", "DamageReduction", 0.5f, 0f, 1f, "How much fall damage a successful fall roll removes. 0.5 means 50% less damage.", "General", "FallRollDamageReduction");
			RequireForwardPress = BindBoolSetting("Wall Jump", "RequireForwardPress", defaultValue: true, "If on, you must be pressing forward to trigger a wall jump.", "General", "RequireForwardPress");
			StaminaDebtEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Stamina Debt", "Enabled", true, "If this is off, Stride leaves stamina spending and the stamina bar alone.");
			EnableVerboseLogging = BindBoolSetting("Logging", "EnableVerboseLogging", defaultValue: false, "Main on/off switch for verbose logs. If this is off, Stride prints no verbose logs at all.", "Debug", "VerboseLogging");
			VerboseAllFeatures = BindBoolSetting("Logging", "VerboseAllFeatures", defaultValue: false, "Turns on verbose logs for every feature at once. This only works while EnableVerboseLogging is on.", "Debug", "VerboseLogging");
			CoyoteTimeVerboseLogging = BindBoolSetting("Coyote Time", "VerboseLogging", defaultValue: false, "Extra logs just for coyote time. This only works while Logging.EnableVerboseLogging is on.", "Debug", "VerboseTimingLogging");
			JumpBufferVerboseLogging = BindBoolSetting("Jump Buffering", "VerboseLogging", defaultValue: false, "Extra logs just for jump buffering. This only works while Logging.EnableVerboseLogging is on.", "Debug", "VerboseTimingLogging");
			WallJumpVerboseLogging = BindBoolSetting("Wall Jump", "VerboseLogging", defaultValue: false, "Extra logs for wall detection and wall jumping. This only works while Logging.EnableVerboseLogging is on.", "Debug", "VerboseWallJumpLogging");
			FallRollVerboseLogging = BindBoolSetting("Fall Roll", "VerboseLogging", defaultValue: false, "Extra logs just for fall rolls. This only works while Logging.EnableVerboseLogging is on.", "Debug", "VerboseTimingLogging");
			StaminaDebtVerboseLogging = BindBoolSetting("Stamina Debt", "VerboseLogging", defaultValue: false, "Extra logs just for stamina debt. This only works while Logging.EnableVerboseLogging is on.", "Debug", "VerboseStaminaDebtLogging");
			MigrateLegacyWallDetectionVerboseSetting();
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in ((BaseUnityPlugin)this).Config)
			{
				bool synchronizedConfig = item.Key.Section != "Logging" && item.Key.Key != "VerboseLogging";
				if (item.Value is ConfigEntry<bool> configEntry)
				{
					ServerConfig.AddConfigEntry<bool>(configEntry).SynchronizedConfig = synchronizedConfig;
				}
				else if (item.Value is ConfigEntry<string> configEntry2)
				{
					ServerConfig.AddConfigEntry<string>(configEntry2).SynchronizedConfig = synchronizedConfig;
				}
			}
			ServerConfig.AddLockingConfigEntry<bool>(((BaseUnityPlugin)this).Config.Bind<bool>("Server", "LockConfiguration", true, "When enabled on the server, only admins can change synchronized gameplay settings. Logging stays local."));
			ServerConfig.SourceOfTruthChanged += delegate(bool local)
			{
				StrideState.ResetAll();
				Log.LogInfo((object)(local ? "Using local Stride settings." : "Receiving server Stride settings."));
			};
			Harmony val = new Harmony("com.b0n3.stride");
			Type[] types = typeof(StridePlugin).Assembly.GetTypes();
			foreach (Type type in types)
			{
				if (type.Namespace == typeof(StridePlugin).Namespace)
				{
					val.CreateClassProcessor(type).Patch();
				}
			}
			Log.LogInfo((object)"Stride loaded.");
		}

		private void EnsureLegacyConfigMigrated()
		{
			string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
			if (!File.Exists(configFilePath))
			{
				string text = Path.Combine(Paths.ConfigPath, "com.b0n3.walljump.cfg");
				if (File.Exists(text))
				{
					File.Copy(text, configFilePath);
					Log.LogInfo((object)("Migrated config from 'com.b0n3.walljump.cfg' to '" + Path.GetFileName(configFilePath) + "'."));
				}
			}
		}

		private FloatSetting BindFloatSetting(string section, string key, float defaultValue, float minValue, float maxValue, string description, string legacySection, string legacyKey)
		{
			ConfigEntry<string> entry = ((BaseUnityPlugin)this).Config.Bind<string>(section, key, FormatFloat(defaultValue), description + " Range: " + FormatFloat(minValue) + " to " + FormatFloat(maxValue) + ". Invalid values are reset automatically.");
			MigrateEntryValue<string>(entry, legacySection, legacyKey);
			return new FloatSetting(entry, defaultValue, minValue, maxValue);
		}

		private ConfigEntry<bool> BindBoolSetting(string section, string key, bool defaultValue, string description, string legacySection, string legacyKey)
		{
			ConfigEntry<bool> val = ((BaseUnityPlugin)this).Config.Bind<bool>(section, key, defaultValue, description);
			MigrateEntryValue<bool>(val, legacySection, legacyKey);
			return val;
		}

		private void MigrateEntryValue<T>(ConfigEntry<T> entry, string legacySection, string legacyKey)
		{
			if (entry == null || string.IsNullOrEmpty(legacySection) || string.IsNullOrEmpty(legacyKey))
			{
				return;
			}
			string key = BuildCompositeKey(((ConfigEntryBase)entry).Definition.Section, ((ConfigEntryBase)entry).Definition.Key);
			if (existingConfigValues.ContainsKey(key))
			{
				return;
			}
			string key2 = BuildCompositeKey(legacySection, legacyKey);
			if (!existingConfigValues.TryGetValue(key2, out var value))
			{
				return;
			}
			try
			{
				T val = (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
				if (!EqualityComparer<T>.Default.Equals(entry.Value, val))
				{
					entry.Value = val;
					((ConfigEntryBase)entry).ConfigFile.Save();
				}
			}
			catch
			{
			}
		}

		private void MigrateLegacyWallDetectionVerboseSetting()
		{
			string key = BuildCompositeKey("Wall Jump", "VerboseLogging");
			if (!existingConfigValues.ContainsKey(key))
			{
				string key2 = BuildCompositeKey("Debug", "VerboseWallDetectionLogging");
				if (existingConfigValues.TryGetValue(key2, out var value) && bool.TryParse(value, out var result) && result && !WallJumpVerboseLogging.Value)
				{
					WallJumpVerboseLogging.Value = true;
					((ConfigEntryBase)WallJumpVerboseLogging).ConfigFile.Save();
				}
			}
		}

		private static Dictionary<string, string> ReadConfigValues(string configFilePath)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			if (string.IsNullOrEmpty(configFilePath) || !File.Exists(configFilePath))
			{
				return dictionary;
			}
			string section = string.Empty;
			string[] array = File.ReadAllLines(configFilePath);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (string.IsNullOrEmpty(text) || text.StartsWith("#"))
				{
					continue;
				}
				if (text.StartsWith("[") && text.EndsWith("]"))
				{
					section = text.Substring(1, text.Length - 2).Trim();
					continue;
				}
				int num = text.IndexOf('=');
				if (num > 0)
				{
					string key = text.Substring(0, num).Trim();
					string value = text.Substring(num + 1).Trim();
					dictionary[BuildCompositeKey(section, key)] = value;
				}
			}
			return dictionary;
		}

		private static string BuildCompositeKey(string section, string key)
		{
			return section + "::" + key;
		}

		private static float GetFloat(ConfigEntry<string> entry, float defaultValue, float minValue, float maxValue)
		{
			string text = entry.Value?.Trim();
			if (string.IsNullOrEmpty(text))
			{
				return WarnAndFallback(entry, text, defaultValue);
			}
			if (!TryParseFloat(text, out var value) || float.IsNaN(value) || float.IsInfinity(value))
			{
				return WarnAndFallback(entry, text, defaultValue);
			}
			if (value < minValue)
			{
				return WarnAndFallback(entry, text, minValue);
			}
			if (value > maxValue)
			{
				return WarnAndFallback(entry, text, maxValue);
			}
			LastInvalidConfigValues.Remove(((ConfigEntryBase)entry).Definition.Key);
			return value;
		}

		private static float WarnAndFallback(ConfigEntry<string> entry, string raw, float fallbackValue)
		{
			string key = ((ConfigEntryBase)entry).Definition.Key;
			string text = raw ?? "<empty>";
			if (!LastInvalidConfigValues.TryGetValue(key, out var value) || value != text)
			{
				LastInvalidConfigValues[key] = text;
				Log.LogWarning((object)("Config value for " + key + " was invalid ('" + text + "'). Using " + FormatFloat(fallbackValue) + " instead."));
			}
			string text2 = FormatFloat(fallbackValue);
			if (ServerConfig.IsSourceOfTruth && !ConfigSync.ProcessingServerUpdate && entry.Value != text2)
			{
				entry.Value = text2;
				((ConfigEntryBase)entry).ConfigFile.Save();
			}
			return fallbackValue;
		}

		private static bool TryParseFloat(string raw, out float value)
		{
			if (!float.TryParse(raw, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out value))
			{
				return float.TryParse(raw, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.CurrentCulture, out value);
			}
			return true;
		}

		private static string FormatFloat(float value)
		{
			return value.ToString("0.###", CultureInfo.InvariantCulture);
		}

		internal static bool IsDebugEnabled(DebugLogFeature feature)
		{
			if (EnableVerboseLogging == null || !EnableVerboseLogging.Value)
			{
				return false;
			}
			if (VerboseAllFeatures != null && VerboseAllFeatures.Value)
			{
				return true;
			}
			switch (feature)
			{
			case DebugLogFeature.CoyoteTime:
				if (CoyoteTimeVerboseLogging != null)
				{
					return CoyoteTimeVerboseLogging.Value;
				}
				return false;
			case DebugLogFeature.JumpBuffering:
				if (JumpBufferVerboseLogging != null)
				{
					return JumpBufferVerboseLogging.Value;
				}
				return false;
			case DebugLogFeature.WallJump:
				if (WallJumpVerboseLogging != null)
				{
					return WallJumpVerboseLogging.Value;
				}
				return false;
			case DebugLogFeature.FallRoll:
				if (FallRollVerboseLogging != null)
				{
					return FallRollVerboseLogging.Value;
				}
				return false;
			case DebugLogFeature.StaminaDebt:
				if (StaminaDebtVerboseLogging != null)
				{
					return StaminaDebtVerboseLogging.Value;
				}
				return false;
			default:
				return false;
			}
		}
	}
	[HarmonyPatch(typeof(Character), "Jump")]
	internal static class CharacterJumpPatch
	{
		private struct JumpAttemptState
		{
			internal bool IsTrackedPlayer;

			internal bool WasGrounded;

			internal float VerticalVelocityBefore;
		}

		private static void Prefix(Character __instance, ref JumpAttemptState __state)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			__state = default(JumpAttemptState);
			if (IsTrackedPlayer(__instance))
			{
				Rigidbody val = PlayerUpdateWallJumpPatch.TryGetBody(__instance);
				__state.IsTrackedPlayer = true;
				__state.WasGrounded = __instance.IsOnGround();
				__state.VerticalVelocityBefore = (((Object)(object)val != (Object)null) ? val.linearVelocity.y : 0f);
			}
		}

		private static void Postfix(Character __instance, JumpAttemptState __state)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			if (__state.IsTrackedPlayer && __state.WasGrounded && IsTrackedPlayer(__instance))
			{
				Rigidbody val = PlayerUpdateWallJumpPatch.TryGetBody(__instance);
				float num = (((Object)(object)val != (Object)null) ? val.linearVelocity.y : 0f);
				bool num2 = !__instance.IsOnGround();
				bool flag = num > 0.1f && num > __state.VerticalVelocityBefore + 0.05f;
				if (num2 || flag)
				{
					StrideState.RecordGroundJump();
				}
			}
		}

		private static bool IsTrackedPlayer(Character character)
		{
			if ((Object)(object)character != (Object)null)
			{
				return (Object)(object)character == (Object)(object)Player.m_localPlayer;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "Update")]
	internal static class PlayerUpdateWallJumpPatch
	{
		private struct WallCandidate
		{
			internal Vector3 Normal;

			internal Vector3 Point;

			internal float Distance;

			internal float PlaneDepth;

			internal float BaseScore;

			internal float VelocityFacingScore;

			internal float CastFacingScore;

			internal int PieceRootId;
		}

		private struct WallCluster
		{
			internal Vector3 Normal;

			internal Vector3 AveragePoint;

			internal float PlaneDepth;

			internal float Score;

			internal float BestCandidateScore;

			internal float BestVelocityFacingScore;

			internal float NearestDistance;

			internal float PlayerPlaneOffset;

			internal float ForwardFlowScore;

			internal float FrontFacePenalty;

			internal float Weight;

			internal bool RejectedForDistance;

			internal int HitCount;

			internal int PieceRootId;
		}

		internal struct WallDetectionResult
		{
			internal Vector3 Normal;

			internal float NearestDistance;
		}

		private struct MovementInputSnapshot
		{
			internal Vector2 RawLocalInput;

			internal Vector2 ClampedLocalInput;

			internal Vector3 WorldDirection;

			internal float Magnitude;

			internal float ForwardAmount;

			internal bool HasDirectionalInput
			{
				get
				{
					if (Magnitude >= 0.1f)
					{
						return ((Vector3)(ref WorldDirection)).sqrMagnitude >= 0.01f;
					}
					return false;
				}
			}
		}

		private const float MaxWallNormalY = 0.25f;

		private const float MinimumFacingDot = 0.15f;

		private const float MinimumMoveInputMagnitude = 0.1f;

		private const int WallCastDirectionCount = 8;

		private const int MaxWallHitsPerCast = 32;

		private const float WallClusterNormalDot = 0.92f;

		private const float WallClusterPlaneTolerance = 0.2f;

		private const float WallClusterFrontFaceTolerance = 0.18f;

		private const float WallClusterPointNeighborhood = 1.35f;

		private const float MinimumWallClusterConfidenceLead = 0.2f;

		private const float ForwardFlowBiasWeight = 6f;

		private const float AlternateFlowPreferenceMargin = 8.5f;

		private const float AlternateFlowPreferenceLead = 0.2f;

		private const float AlternateFlowMinimum = 0.45f;

		private const float AlternateFlowMinimumFacing = 0.05f;

		private const float MinimumForwardStickY = 0.35f;

		private const float MinimumVelocityScoreMagnitude = 0.35f;

		private const float SingleHitClusterPenalty = 1.1f;

		private const float StrongSingleHitFacing = 0.9f;

		private const float StrongSingleHitDistance = 0.06f;

		private const float MinimumBounceFloorScale = 0.1f;

		private const float MaximumRememberedApproachBoostRatio = 1.25f;

		private const float MaximumOutwardFloorBoost = 1.5f;

		private const float MaximumHorizontalFloorBoost = 2f;

		internal const float StickyWallNormalDot = 0.8f;

		internal const float StickyWallDistanceTolerance = 0.12f;

		internal const float MinimumApproachVelocityDot = 0.05f;

		internal const float MinimumRememberedWallApproachDot = 0.05f;

		private const float MaximumInputBiasFraction = 0.35f;

		private static readonly FieldRef<Character, Rigidbody> CharacterBodyRef = AccessTools.FieldRefAccess<Character, Rigidbody>("m_body");

		private static readonly FieldRef<Character, SEMan> CharacterSeManRef = AccessTools.FieldRefAccess<Character, SEMan>("m_seman");

		private static readonly FieldRef<Character, float> CharacterSpeedRef = AccessTools.FieldRefAccess<Character, float>("m_speed");

		private static readonly FieldRef<Character, float> LastGroundTouchRef = AccessTools.FieldRefAccess<Character, float>("m_lastGroundTouch");

		private static readonly FieldRef<Character, float> MaxAirAltitudeRef = AccessTools.FieldRefAccess<Character, float>("m_maxAirAltitude");

		private static readonly FieldRef<Character, float> JumpStaminaUsageRef = AccessTools.FieldRefAccess<Character, float>("m_jumpStaminaUsage");

		private static readonly FieldRef<Character, float> JumpForceTiredFactorRef = AccessTools.FieldRefAccess<Character, float>("m_jumpForceTiredFactor");

		private static Player trackedPlayer;

		private static bool wasGroundedLastFrame;

		private static readonly List<WallCandidate> WallCandidateBuffer = new List<WallCandidate>(16);

		private static readonly List<WallCluster> WallClusterBuffer = new List<WallCluster>(16);

		private static readonly Vector3[] WallCastDirectionBuffer = (Vector3[])(object)new Vector3[8];

		private static readonly RaycastHit[] WallHitBuffer = (RaycastHit[])(object)new RaycastHit[32];

		private static void Postfix(Player __instance)
		{
			//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_00b6: 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_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			if ((Object)(object)__instance != (Object)(object)trackedPlayer)
			{
				trackedPlayer = __instance;
				wasGroundedLastFrame = false;
				StrideState.ResetAll();
				StaminaDebt.ResetTracking();
			}
			bool flag = ((Character)__instance).IsOnGround();
			bool flag2 = StrideState.TryConsumeResolvedJumpPress();
			if (flag || ((Character)__instance).IsSwimming() || ((Character)__instance).IsDead())
			{
				if (flag)
				{
					StrideState.RecordGrounded();
					if (!wasGroundedLastFrame && StrideState.TryConsumeBufferedGroundJump())
					{
						LogDebug(StridePlugin.DebugLogFeature.JumpBuffering, "Jump buffer triggered.");
						((Character)__instance).Jump(false);
					}
				}
				wasGroundedLastFrame = flag;
				StrideState.ResetAirborneState();
				return;
			}
			wasGroundedLastFrame = false;
			Rigidbody val = TryGetBody((Character)(object)__instance);
			Vector3 horizontalVelocity = (((Object)(object)val != (Object)null) ? Vector3.ProjectOnPlane(val.linearVelocity, Vector3.up) : Vector3.zero);
			MovementInputSnapshot movementInput = SampleMovementInput(__instance);
			int num;
			if (StridePlugin.WallJumpEnabled != null)
			{
				num = (StridePlugin.WallJumpEnabled.Value ? 1 : 0);
				if (num != 0)
				{
					goto IL_00e0;
				}
			}
			else
			{
				num = 0;
			}
			StrideState.ClearWallJumpTracking();
			goto IL_00e0;
			IL_00e0:
			WallDetectionResult wallDetection = default(WallDetectionResult);
			bool flag3 = num != 0 && TryFindWall(__instance, flag2, out wallDetection);
			if (flag3)
			{
				StrideState.RecordWallContact(wallDetection.Normal, horizontalVelocity, wallDetection.NearestDistance);
			}
			if (flag2 && StrideState.UsesBufferedJumpTracking())
			{
				StrideState.RecordJumpPressed();
			}
			if (!flag2)
			{
				return;
			}
			if (StrideState.TryConsumeCoyoteJump())
			{
				LastGroundTouchRef.Invoke((Character)(object)__instance) = 0f;
				StrideState.RecordCoyoteJump();
				LogDebug(StridePlugin.DebugLogFeature.CoyoteTime, "Coyote jump triggered.");
				((Character)__instance).Jump(false);
			}
			else
			{
				if (!StrideState.CanWallJump(__instance) || (StridePlugin.RequireForwardPress.Value && !IsPressingForward(movementInput)))
				{
					return;
				}
				if (!StrideState.TryGetWallJumpContact(__instance, flag3, wallDetection, out var wallContact, out var usingRememberedWall))
				{
					LogDebug(StridePlugin.DebugLogFeature.WallJump, "Jump pressed in air, but no valid wall was found.");
					return;
				}
				LogDebug(StridePlugin.DebugLogFeature.WallJump, "Wall jump using " + (usingRememberedWall ? "remembered" : "current") + " wall normal " + FormatVector(wallContact.Normal) + ".");
				if (!(((Vector3)(ref wallContact.Normal)).sqrMagnitude < 0.01f))
				{
					PerformWallJump(__instance, wallContact, movementInput);
				}
			}
		}

		private static bool IsPressingForward(MovementInputSnapshot movementInput)
		{
			return movementInput.ForwardAmount >= 0.35f;
		}

		private static bool TryFindWall(Player player, bool logDetails, out WallDetectionResult wallDetection)
		{
			//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_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: 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_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: 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_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: 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)
			wallDetection = default(WallDetectionResult);
			Vector3 centerPoint = ((Character)player).GetCenterPoint();
			Vector3 forward = ((Component)player).transform.forward;
			forward.y = 0f;
			((Vector3)(ref forward)).Normalize();
			Vector3 right = ((Component)player).transform.right;
			right.y = 0f;
			((Vector3)(ref right)).Normalize();
			Rigidbody val = TryGetBody((Character)(object)player);
			Vector3 val2 = (((Object)(object)val != (Object)null) ? Vector3.ProjectOnPlane(val.linearVelocity, Vector3.up) : Vector3.zero);
			Vector3 velocityDirection = val2;
			velocityDirection.y = 0f;
			if (((Vector3)(ref velocityDirection)).sqrMagnitude > 0.01f)
			{
				((Vector3)(ref velocityDirection)).Normalize();
			}
			PopulateWallCastDirections(forward, right, velocityDirection, WallCastDirectionBuffer);
			WallCandidateBuffer.Clear();
			float num = Mathf.Max(0.05f, StridePlugin.WallCheckRadius);
			for (int i = 0; i < WallCastDirectionBuffer.Length; i++)
			{
				Vector3 val3 = WallCastDirectionBuffer[i];
				if (((Vector3)(ref val3)).sqrMagnitude < 0.01f)
				{
					continue;
				}
				int num2 = Physics.SphereCastNonAlloc(centerPoint, num, val3, WallHitBuffer, StridePlugin.WallCheckDistance, -1, (QueryTriggerInteraction)1);
				if (num2 > 0)
				{
					if (num2 == WallHitBuffer.Length && logDetails)
					{
						LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Wall cast hit buffer filled at {WallHitBuffer.Length} hits; some wall candidates may be skipped.");
					}
					AddWallCandidates(player, val3, val2, WallHitBuffer, num2, WallCandidateBuffer, logDetails);
				}
			}
			if (WallCandidateBuffer.Count == 0)
			{
				return false;
			}
			return TryGetSupportedWallNormal(player, WallCandidateBuffer, logDetails, out wallDetection);
		}

		private static void AddWallCandidates(Player player, Vector3 castDirection, Vector3 horizontalVelocity, RaycastHit[] hits, int hitCount, List<WallCandidate> candidates, bool logDetails)
		{
			//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_0019: 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_002f: 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_0031: 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_0094: 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_00b6: 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)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			if (hits == null || hitCount <= 0)
			{
				return;
			}
			Vector3 normalized = ((Vector3)(ref castDirection)).normalized;
			for (int i = 0; i < hitCount; i++)
			{
				if (TryGetWallHitNormal(player, normalized, hits[i], logDetails, out var wallNormal))
				{
					float num = Vector3.Dot(normalized, -wallNormal);
					float num2 = 0f;
					if (((Vector3)(ref horizontalVelocity)).sqrMagnitude >= 0.122499995f)
					{
						num2 = Vector3.Dot(((Vector3)(ref horizontalVelocity)).normalized, -wallNormal);
					}
					float num3 = Mathf.Clamp01(((RaycastHit)(ref hits[i])).distance / Mathf.Max(0.01f, StridePlugin.WallCheckDistance));
					Vector3 point = ((RaycastHit)(ref hits[i])).point;
					Piece componentInParent = ((Component)((RaycastHit)(ref hits[i])).collider).GetComponentInParent<Piece>();
					candidates.Add(new WallCandidate
					{
						Normal = wallNormal,
						Point = point,
						Distance = ((RaycastHit)(ref hits[i])).distance,
						PlaneDepth = Vector3.Dot(wallNormal, point),
						BaseScore = num2 * 1.75f + num * 0.35f - num3 * 0.75f,
						VelocityFacingScore = num2,
						CastFacingScore = num,
						PieceRootId = GetPieceRootId(componentInParent, ((RaycastHit)(ref hits[i])).collider)
					});
				}
			}
		}

		private static void PopulateWallCastDirections(Vector3 fallbackDirection, Vector3 rightDirection, Vector3 velocityDirection, Vector3[] directions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//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_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_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)
			//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_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_008a: 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_0096: 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_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			directions[0] = fallbackDirection;
			directions[1] = velocityDirection;
			directions[2] = Quaternion.Euler(0f, 30f, 0f) * fallbackDirection;
			directions[3] = Quaternion.Euler(0f, -30f, 0f) * fallbackDirection;
			directions[4] = Quaternion.Euler(0f, 60f, 0f) * fallbackDirection;
			directions[5] = Quaternion.Euler(0f, -60f, 0f) * fallbackDirection;
			directions[6] = rightDirection;
			directions[7] = -rightDirection;
		}

		private static bool TryGetSupportedWallNormal(Player player, List<WallCandidate> candidates, bool logDetails, out WallDetectionResult wallDetection)
		{
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			wallDetection = default(WallDetectionResult);
			BuildWallClusters(player, candidates, WallClusterBuffer);
			if (WallClusterBuffer.Count == 0)
			{
				return false;
			}
			int num = -1;
			float num2 = float.MinValue;
			float num3 = float.MinValue;
			for (int i = 0; i < WallClusterBuffer.Count; i++)
			{
				WallCluster wallCluster = WallClusterBuffer[i];
				if (wallCluster.FrontFacePenalty > 0f)
				{
					continue;
				}
				float score = wallCluster.Score;
				if (score <= num2)
				{
					if (score > num3)
					{
						num3 = score;
					}
				}
				else
				{
					num3 = num2;
					num2 = score;
					num = i;
				}
			}
			if (num < 0)
			{
				return false;
			}
			WallCluster wallCluster2 = WallClusterBuffer[num];
			int num4 = FindPreferredFlowCluster(WallClusterBuffer, num, num2);
			bool flag = false;
			if (num4 >= 0)
			{
				WallCluster wallCluster3 = WallClusterBuffer[num4];
				if (logDetails)
				{
					LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Switching wall cluster from {num + 1} to {num4 + 1} to preserve along-wall flow. Original flow {wallCluster2.ForwardFlowScore:0.###}, preferred flow {wallCluster3.ForwardFlowScore:0.###}.");
				}
				num = num4;
				num2 = wallCluster3.Score;
				wallCluster2 = wallCluster3;
				flag = true;
			}
			float num5 = num2 - num3;
			if (!flag && WallClusterBuffer.Count > 1 && num5 < 0.2f)
			{
				if (logDetails)
				{
					LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Rejected wall cluster with weak confidence lead {num5:0.###}. Best score {num2:0.###}, runner-up {num3:0.###}.");
					LogWallClusters(WallClusterBuffer);
				}
				return false;
			}
			if (flag && logDetails)
			{
				LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Accepted flow-preserving wall cluster despite confidence lead {num5:0.###}.");
			}
			wallDetection = new WallDetectionResult
			{
				Normal = wallCluster2.Normal,
				NearestDistance = wallCluster2.NearestDistance
			};
			if (logDetails)
			{
				LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Selected wall normal {FormatVector(wallDetection.Normal)} from cluster {num + 1}/{WallClusterBuffer.Count} with {wallCluster2.HitCount} hit(s), nearest {wallCluster2.NearestDistance:0.###}, flow {wallCluster2.ForwardFlowScore:0.###}, score {wallCluster2.Score:0.###}.");
			}
			return true;
		}

		private static int FindPreferredFlowCluster(List<WallCluster> clusters, int bestIndex, float bestScore)
		{
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: 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)
			if (bestIndex < 0 || bestIndex >= clusters.Count)
			{
				return -1;
			}
			WallCluster wallCluster = clusters[bestIndex];
			if (wallCluster.ForwardFlowScore >= 0.45f || wallCluster.BestVelocityFacingScore < 0.75f)
			{
				return -1;
			}
			int result = -1;
			float num = float.MinValue;
			for (int i = 0; i < clusters.Count; i++)
			{
				if (i == bestIndex)
				{
					continue;
				}
				WallCluster wallCluster2 = clusters[i];
				if (wallCluster2.Score < bestScore - 8.5f || wallCluster2.ForwardFlowScore < 0.45f || wallCluster2.ForwardFlowScore < wallCluster.ForwardFlowScore + 0.2f || wallCluster2.BestVelocityFacingScore < 0.05f)
				{
					continue;
				}
				bool num2 = wallCluster2.PieceRootId != 0 && wallCluster2.PieceRootId == wallCluster.PieceRootId;
				Vector3 val = wallCluster2.AveragePoint - wallCluster.AveragePoint;
				bool flag = ((Vector3)(ref val)).sqrMagnitude <= 7.2900004f;
				if (num2 || flag)
				{
					float num3 = wallCluster2.Score + wallCluster2.ForwardFlowScore * 3f + wallCluster2.BestVelocityFacingScore;
					if (!(num3 <= num))
					{
						num = num3;
						result = i;
					}
				}
			}
			return result;
		}

		private static void BuildWallClusters(Player player, List<WallCandidate> candidates, List<WallCluster> clusters)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0034: 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_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: 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_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: 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_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: 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_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0354: 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_036e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0375: Unknown result type (might be due to invalid IL or missing references)
			//IL_037a: Unknown result type (might be due to invalid IL or missing references)
			//IL_037f: Unknown result type (might be due to invalid IL or missing references)
			clusters.Clear();
			Vector3 position = ((Component)player).transform.position;
			Rigidbody val = TryGetBody((Character)(object)player);
			Vector3 horizontalVelocity = (((Object)(object)val != (Object)null) ? Vector3.ProjectOnPlane(val.linearVelocity, Vector3.up) : Vector3.zero);
			for (int i = 0; i < candidates.Count; i++)
			{
				WallCandidate candidate = candidates[i];
				int num = -1;
				for (int j = 0; j < clusters.Count; j++)
				{
					if (CanJoinCluster(candidate, clusters[j]))
					{
						num = j;
						break;
					}
				}
				if (num < 0)
				{
					clusters.Add(new WallCluster
					{
						Normal = candidate.Normal,
						PlaneDepth = candidate.PlaneDepth,
						AveragePoint = candidate.Point,
						Score = candidate.BaseScore,
						BestCandidateScore = candidate.BaseScore,
						BestVelocityFacingScore = candidate.VelocityFacingScore,
						NearestDistance = candidate.Distance,
						Weight = 1f,
						HitCount = 1,
						PieceRootId = candidate.PieceRootId
					});
					continue;
				}
				WallCluster value = clusters[num];
				float num2 = Mathf.Max(0.1f, candidate.BaseScore + 2f);
				float num3 = value.Weight + num2;
				Vector3 val2 = value.Normal * value.Weight + candidate.Normal * num2;
				value.Normal = ((Vector3)(ref val2)).normalized;
				value.PlaneDepth = (value.PlaneDepth * value.Weight + candidate.PlaneDepth * num2) / num3;
				value.AveragePoint = (value.AveragePoint * value.Weight + candidate.Point * num2) / num3;
				value.Weight = num3;
				value.Score += candidate.BaseScore;
				value.HitCount++;
				value.NearestDistance = Mathf.Min(value.NearestDistance, candidate.Distance);
				if (candidate.BaseScore > value.BestCandidateScore)
				{
					value.BestCandidateScore = candidate.BaseScore;
					value.BestVelocityFacingScore = candidate.VelocityFacingScore;
					value.PieceRootId = candidate.PieceRootId;
				}
				clusters[num] = value;
			}
			for (int k = 0; k < clusters.Count; k++)
			{
				WallCluster value2 = clusters[k];
				if (value2.NearestDistance > StridePlugin.WallJumpTriggerDistance)
				{
					value2.RejectedForDistance = true;
					value2.Score = float.MinValue;
					clusters[k] = value2;
				}
				else
				{
					value2.Score += (float)value2.HitCount * 0.45f;
					value2.PlayerPlaneOffset = value2.PlaneDepth - Vector3.Dot(value2.Normal, position);
					value2.ForwardFlowScore = CalculateForwardFlowScore(horizontalVelocity, value2.Normal);
					clusters[k] = value2;
				}
			}
			for (int l = 0; l < clusters.Count; l++)
			{
				WallCluster value3 = clusters[l];
				if (value3.RejectedForDistance)
				{
					continue;
				}
				float num4 = 0f;
				for (int m = 0; m < clusters.Count; m++)
				{
					if (l == m)
					{
						continue;
					}
					WallCluster wallCluster = clusters[m];
					if (!(Vector3.Dot(value3.Normal, wallCluster.Normal) < 0.92f))
					{
						Vector3 val3 = wallCluster.AveragePoint - value3.AveragePoint;
						if (!(((Vector3)(ref val3)).sqrMagnitude > 1.8225001f) && wallCluster.PlayerPlaneOffset > 0f && value3.PlayerPlaneOffset > 0f && wallCluster.PlayerPlaneOffset + 0.18f < value3.PlayerPlaneOffset)
						{
							num4 = Mathf.Max(num4, value3.PlayerPlaneOffset - wallCluster.PlayerPlaneOffset);
						}
					}
				}
				value3.FrontFacePenalty = num4;
				value3.Score -= num4 * 8f;
				value3.Score += Mathf.Clamp01(1f - value3.NearestDistance / Mathf.Max(0.01f, StridePlugin.WallCheckDistance)) * 0.6f;
				value3.Score += (float)Mathf.Min(value3.HitCount, 4) * 0.15f;
				if (value3.HitCount == 1 && (value3.BestVelocityFacingScore < 0.9f || value3.NearestDistance > 0.06f))
				{
					value3.Score -= 1.1f;
				}
				value3.Score += value3.ForwardFlowScore * 6f;
				clusters[l] = value3;
			}
		}

		private static bool CanJoinCluster(WallCandidate candidate, WallCluster cluster)
		{
			//IL_0001: 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)
			if (Vector3.Dot(candidate.Normal, cluster.Normal) < 0.92f)
			{
				return false;
			}
			if (Mathf.Abs(candidate.PlaneDepth - cluster.PlaneDepth) > 0.2f)
			{
				return false;
			}
			return true;
		}

		private static float CalculateForwardFlowScore(Vector3 horizontalVelocity, Vector3 wallNormal)
		{
			//IL_0014: 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_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_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: 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_00af: 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_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			if (((Vector3)(ref horizontalVelocity)).sqrMagnitude < 0.122499995f)
			{
				return 0f;
			}
			Vector3 val = wallNormal;
			val.y = 0f;
			if (((Vector3)(ref val)).sqrMagnitude < 0.01f)
			{
				return 0f;
			}
			((Vector3)(ref val)).Normalize();
			Vector3 normalized = ((Vector3)(ref horizontalVelocity)).normalized;
			float num = Mathf.Max(0f, Vector3.Dot(normalized, -val));
			Vector3 val2 = Vector3.ProjectOnPlane(normalized, val);
			if (((Vector3)(ref val2)).sqrMagnitude < 0.0001f)
			{
				return 0f;
			}
			((Vector3)(ref val2)).Normalize();
			Vector3 val3 = Vector3.Reflect(normalized, val);
			val3.y = 0f;
			if (((Vector3)(ref val3)).sqrMagnitude < 0.0001f)
			{
				return 0f;
			}
			((Vector3)(ref val3)).Normalize();
			float num2 = Vector3.Dot(val3, val2);
			float num3 = Vector3.Dot(val3, normalized);
			return num2 * 0.75f + num3 * 0.25f - num * 0.15f;
		}

		private static bool TryGetWallHitNormal(Player player, Vector3 castDirection, RaycastHit hit, bool logDetails, out Vector3 wallNormal)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: 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_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_004a: 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_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_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			wallNormal = default(Vector3);
			if ((Object)(object)((RaycastHit)(ref hit)).collider == (Object)null)
			{
				return false;
			}
			Character componentInParent = ((Component)((RaycastHit)(ref hit)).collider).GetComponentInParent<Character>();
			if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent == (Object)(object)player)
			{
				return false;
			}
			Vector3 normal = ((RaycastHit)(ref hit)).normal;
			Vector3 normalized = ((Vector3)(ref normal)).normalized;
			if (Mathf.Abs(normalized.y) > 0.25f)
			{
				return false;
			}
			if (Vector3.Dot(castDirection, -normalized) < 0.15f)
			{
				return false;
			}
			wallNormal = normalized;
			return true;
		}

		private static void PerformWallJump(Player player, StrideState.WallContactSnapshot wallContact, MovementInputSnapshot movementInput)
		{
			//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_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_0064: 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_006f: 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_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: 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)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: 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_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: 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_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			Rigidbody val = TryGetBody((Character)(object)player);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Vector3 away = wallContact.Normal;
			away.y = 0f;
			((Vector3)(ref away)).Normalize();
			if (((Vector3)(ref away)).sqrMagnitude < 0.01f)
			{
				away = -((Component)player).transform.forward;
				away.y = 0f;
				((Vector3)(ref away)).Normalize();
			}
			Vector3 linearVelocity = val.linearVelocity;
			Vector3 val2 = Vector3.ProjectOnPlane(linearVelocity, Vector3.up);
			bool flag = false;
			if (((Vector3)(ref wallContact.ApproachVelocity)).sqrMagnitude > 0.01f && wallContact.ApproachTime > -100f && Time.time - wallContact.ApproachTime <= StridePlugin.CoyoteTimeSeconds && ((Vector3)(ref wallContact.ApproachVelocity)).magnitude > ((Vector3)(ref val2)).magnitude)
			{
				float num = Mathf.Max(0f, Time.time - wallContact.ApproachTime);
				float num2 = Mathf.Clamp01(1f - num / Mathf.Max(0.01f, StridePlugin.CoyoteTimeSeconds));
				num2 *= num2;
				Vector3 val3 = Vector3.Lerp(val2, wallContact.ApproachVelocity, num2);
				float magnitude = ((Vector3)(ref val2)).magnitude;
				float num3 = ((magnitude > 0.01f) ? (magnitude * 1.25f) : ((Vector3)(ref wallContact.ApproachVelocity)).magnitude);
				float magnitude2 = ((Vector3)(ref val3)).magnitude;
				if (magnitude2 > num3 && magnitude2 > 0.01f)
				{
					val3 = ((Vector3)(ref val3)).normalized * num3;
				}
				val2 = val3;
				flag = true;
			}
			float magnitude3 = ((Vector3)(ref val2)).magnitude;
			if (magnitude3 < StridePlugin.MinimumWallJumpEntrySpeed)
			{
				LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Allowing wall jump despite low incoming horizontal speed {magnitude3:0.###} because wall contact is valid.");
			}
			bool isTired = IsVanillaTiredJump(player, linearVelocity);
			Vector3 currentVelocity = default(Vector3);
			((Vector3)(ref currentVelocity))..ctor(val2.x, linearVelocity.y, val2.z);
			Vector3 val4 = BuildWallJumpVelocity(player, currentVelocity, away, isTired, movementInput);
			LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Wall jump incoming velocity {FormatVector(linearVelocity)} speed {((Vector3)(ref linearVelocity)).magnitude:0.###} direction {FormatDirection(linearVelocity)}.");
			if (flag)
			{
				LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Wall jump used decayed stored wall approach horizontal {FormatVector(val2)} speed {magnitude3:0.###}.");
			}
			LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Wall jump launch input local {FormatVector2(movementInput.RawLocalInput)} clamped {FormatVector2(movementInput.ClampedLocalInput)} magnitude {movementInput.Magnitude:0.###} forward {movementInput.ForwardAmount:0.###} world {FormatVector(movementInput.WorldDirection)}.");
			LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Wall jump outgoing velocity {FormatVector(val4)} speed {((Vector3)(ref val4)).magnitude:0.###} direction {FormatDirection(val4)}.");
			LogDebug(StridePlugin.DebugLogFeature.WallJump, BuildWallJumpLaunchReport(wallContact, away, linearVelocity, val2, val4, movementInput, flag));
			MaxAirAltitudeRef.Invoke((Character)(object)player) = ((Component)player).transform.position.y;
			((Character)player).ForceJump(val4, true);
			StrideState.RecordWallJump();
			LogDebug(StridePlugin.DebugLogFeature.WallJump, "Wall jump triggered.");
		}

		private static bool IsVanillaTiredJump(Player player, Vector3 currentVelocity)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			float num = JumpStaminaUsageRef.Invoke((Character)(object)player);
			bool num2 = !((Character)player).HaveStamina(num);
			if (num2 && ((Character)player).IsPlayer())
			{
				Hud instance = Hud.instance;
				if (instance != null)
				{
					instance.StaminaBarEmptyFlash();
				}
			}
			float num3 = CharacterSpeedRef.Invoke((Character)(object)player);
			SEMan val = CharacterSeManRef.Invoke((Character)(object)player);
			if (val != null)
			{
				val.ApplyStatusEffectSpeedMods(ref num3, currentVelocity);
			}
			if (!num2)
			{
				return num3 <= 0f;
			}
			return true;
		}

		private static Vector3 BuildWallJumpVelocity(Player player, Vector3 currentVelocity, Vector3 away, bool isTired, MovementInputSnapshot movementInput)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0014: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: 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_017c: 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_0184: 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_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: 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)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = Vector3.ProjectOnPlane(currentVelocity, Vector3.up);
			float magnitude = ((Vector3)(ref val)).magnitude;
			float num = Vector3.Dot(val, away);
			float num2 = Mathf.Max(0f, 0f - num);
			float num3 = Mathf.Max(0f, num);
			Vector3 val2 = val - away * num + away * (num3 + num2 * StridePlugin.ReflectionResponsiveness);
			if (movementInput.HasDirectionalInput)
			{
				Vector3 worldDirection = movementInput.WorldDirection;
				float magnitude2 = movementInput.Magnitude;
				Vector3 val3 = Vector3.ProjectOnPlane(val2, away);
				Vector3 val4 = Vector3.ProjectOnPlane(worldDirection, away);
				if (((Vector3)(ref val4)).sqrMagnitude > 0.01f && (((Vector3)(ref val3)).sqrMagnitude < 0.01f || Vector3.Dot(((Vector3)(ref val3)).normalized, ((Vector3)(ref val4)).normalized) >= -0.1f))
				{
					((Vector3)(ref val4)).Normalize();
					float num4 = Mathf.Max(0.1f, Mathf.Min(StridePlugin.HorizontalBounceMinSpeed * StridePlugin.InputBiasFactor, ((Vector3)(ref val2)).magnitude * 0.35f));
					val2 += val4 * (num4 * magnitude2);
				}
			}
			float num5 = Mathf.Lerp(0.1f, 1f, Mathf.Clamp01(magnitude / Mathf.Max(0.01f, StridePlugin.HorizontalBounceMinSpeed)));
			float num6 = Mathf.Max(num2, StridePlugin.OutwardBounceMinSpeed * num5);
			float num7 = Mathf.Max(magnitude, StridePlugin.HorizontalBounceMinSpeed * num5);
			float num8 = Mathf.Min(num6, Mathf.Max(num2, magnitude + 1.5f));
			float num9 = Mathf.Min(num7, magnitude + 2f);
			float num10 = Vector3.Dot(val2, away);
			if (num10 < num8)
			{
				val2 += away * (num8 - num10);
			}
			float magnitude3 = ((Vector3)(ref val2)).magnitude;
			if (magnitude3 < num9)
			{
				val2 = ((magnitude3 > 0.01f) ? (val2 / magnitude3) : away) * num9;
			}
			float num11 = Mathf.Max(currentVelocity.y * 0.25f, StridePlugin.VerticalPushForce);
			Vector3 val5 = val2 + Vector3.up * num11;
			if (isTired)
			{
				val5 *= JumpForceTiredFactorRef.Invoke((Character)(object)player);
			}
			SEMan val6 = CharacterSeManRef.Invoke((Character)(object)player);
			if (val6 != null)
			{
				val6.ApplyStatusEffectJumpMods(ref val5);
			}
			return val5;
		}

		private static MovementInputSnapshot SampleMovementInput(Player player)
		{
			//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_00a1: 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_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: 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_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: 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_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: 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_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			MovementInputSnapshot result = default(MovementInputSnapshot);
			Vector2 val = Vector2.zero;
			if (ZInput.GetButton("Forward"))
			{
				val.y += 1f;
			}
			if (ZInput.GetButton("Backward"))
			{
				val.y -= 1f;
			}
			if (ZInput.GetButton("Right"))
			{
				val.x += 1f;
			}
			if (ZInput.GetButton("Left"))
			{
				val.x -= 1f;
			}
			val.x += ZInput.GetJoyLeftStickX();
			val.y += 0f - ZInput.GetJoyLeftStickY();
			result.RawLocalInput = val;
			result.ForwardAmount = Mathf.Clamp01(Mathf.Max(val.y, 0f));
			result.Magnitude = Mathf.Clamp01(((Vector2)(ref val)).magnitude);
			if (result.Magnitude < 0.1f)
			{
				return result;
			}
			val = (result.ClampedLocalInput = Vector2.ClampMagnitude(val, 1f));
			Transform obj = (((Object)(object)GameCamera.instance != (Object)null) ? ((Component)GameCamera.instance).transform : ((Component)player).transform);
			Vector3 forward = obj.forward;
			forward.y = 0f;
			if (((Vector3)(ref forward)).sqrMagnitude < 0.01f)
			{
				forward = ((Component)player).transform.forward;
				forward.y = 0f;
			}
			((Vector3)(ref forward)).Normalize();
			Vector3 right = obj.right;
			right.y = 0f;
			if (((Vector3)(ref right)).sqrMagnitude < 0.01f)
			{
				right = ((Component)player).transform.right;
				right.y = 0f;
			}
			((Vector3)(ref right)).Normalize();
			Vector3 val2 = forward * val.y + right * val.x;
			if (((Vector3)(ref val2)).sqrMagnitude < 0.01f)
			{
				result.Magnitude = 0f;
				result.ForwardAmount = 0f;
				result.ClampedLocalInput = Vector2.zero;
				result.WorldDirection = Vector3.zero;
				return result;
			}
			result.WorldDirection = ((Vector3)(ref val2)).normalized;
			return result;
		}

		private static void LogDebug(StridePlugin.DebugLogFeature feature, string message)
		{
			if (StridePlugin.IsDebugEnabled(feature))
			{
				StridePlugin.Log.LogInfo((object)message);
			}
		}

		private static void LogWallClusters(List<WallCluster> clusters)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < clusters.Count; i++)
			{
				WallCluster wallCluster = clusters[i];
				LogDebug(StridePlugin.DebugLogFeature.WallJump, $"Cluster {i + 1}: normal {FormatVector(wallCluster.Normal)}, hits {wallCluster.HitCount}, plane offset {wallCluster.PlayerPlaneOffset:0.###}, nearest {wallCluster.NearestDistance:0.###}, score {wallCluster.Score:0.###}, best facing {wallCluster.BestVelocityFacingScore:0.###}, flow {wallCluster.ForwardFlowScore:0.###}, penalty {wallCluster.FrontFacePenalty:0.###}, rejectedDistance {wallCluster.RejectedForDistance}, piece {wallCluster.PieceRootId}.");
			}
		}

		private static int GetPieceRootId(Piece piece, Collider collider)
		{
			if ((Object)(object)piece != (Object)null)
			{
				return ((Object)((Component)piece).gameObject).GetInstanceID();
			}
			if ((Object)(object)collider != (Object)null)
			{
				return ((Object)((Component)((Component)collider).transform.root).gameObject).GetInstanceID();
			}
			return 0;
		}

		internal static string FormatVector(Vector3 value)
		{
			//IL_0005: 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_001b: Unknown result type (might be due to invalid IL or missing references)
			return $"({value.x:0.###}, {value.y:0.###}, {value.z:0.###})";
		}

		internal static string FormatVector2(Vector2 value)
		{
			//IL_0005: 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)
			return $"({value.x:0.###}, {value.y:0.###})";
		}

		private static string BuildWallJumpLaunchReport(StrideState.WallContactSnapshot wallContact, Vector3 away, Vector3 currentVelocity, Vector3 launchHorizontalVelocity, Vector3 jumpVelocity, MovementInputSnapshot movementInput, bool usedSnapshotApproachVelocity)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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_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_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_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: 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)
			//IL_006d: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: 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_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = Vector3.ProjectOnPlane(currentVelocity, Vector3.up);
			Vector3 val2 = Vector3.ProjectOnPlane(jumpVelocity, Vector3.up);
			float num = Vector3.Dot(val, away);
			float num2 = Vector3.Dot(launchHorizontalVelocity, away);
			float num3 = Vector3.Dot(val2, away);
			float num4 = CalculateTangentialSpeed(val, away);
			float num5 = CalculateTangentialSpeed(launchHorizontalVelocity, away);
			float num6 = CalculateTangentialSpeed(val2, away);
			float y = jumpVelocity.y;
			float num7 = (movementInput.HasDirectionalInput ? Vector3.Dot(movementInput.WorldDirection, away) : 0f);
			return string.Format("Wall jump report | wall {0} near {1:0.###} | input raw {2} clamp {3} mag {4:0.###} fwd {5:0.###} align {6:0.###} world {7} | inH {8} outDot {9:0.###} tan {10:0.###} | launchH {11} outDot {12:0.###} tan {13:0.###} mem {14} | outH {15} outDot {16:0.###} tan {17:0.###} up {18:0.###}", FormatVector(wallContact.Normal), wallContact.NearestDistance, FormatVector2(movementInput.RawLocalInput), FormatVector2(movementInput.ClampedLocalInput), movementInput.Magnitude, movementInput.ForwardAmount, num7, FormatVector(movementInput.WorldDirection), FormatVector(val), num, num4, FormatVector(launchHorizontalVelocity), num2, num5, usedSnapshotApproachVelocity ? "yes" : "no", FormatVector(val2), num3, num6, y);
		}

		private static float CalculateTangentialSpeed(Vector3 velocity, Vector3 away)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: 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_0013: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = velocity - away * Vector3.Dot(velocity, away);
			return ((Vector3)(ref val)).magnitude;
		}

		private static string FormatDirection(Vector3 value)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if (((Vector3)(ref value)).sqrMagnitude < 0.0001f)
			{
				return "(0, 0, 0)";
			}
			return FormatVector(((Vector3)(ref value)).normalized);
		}

		internal static Rigidbody TryGetBody(Character character)
		{
			if (!((Object)(object)character == (Object)null))
			{
				return CharacterBodyRef.Invoke(character);
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(Player), "SetControls")]
	internal static class PlayerSetControlsJumpIntentPatch
	{
		private static Player trackedPlayer;

		private static bool lastResolvedJumpIntent;

		private static void Postfix(Player __instance, bool blockHold, ref bool jump, ref bool dodge)
		{
			if ((Object)(object)__instance != (Object)(object)trackedPlayer)
			{
				trackedPlayer = __instance;
				lastResolvedJumpIntent = false;
			}
			if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || ((Character)__instance).IsDead() || ((Character)__instance).IsSwimming())
			{
				lastResolvedJumpIntent = false;
				return;
			}
			bool num = !((Character)__instance).IsOnGround();
			string triggerSource;
			bool flag = jump && !PlayerSetControlsFallRollPatch.TryGetDodgeIntent(__instance, blockHold, jump, dodge, out triggerSource);
			if (num && flag && !lastResolvedJumpIntent && StrideState.ShouldAcceptResolvedAirborneJumpPress())
			{
				StrideState.RecordResolvedJumpPress();
			}
			lastResolvedJumpIntent = jump;
		}
	}
	[HarmonyPatch(typeof(Player), "SetControls")]
	internal static class PlayerSetControlsFallRollPatch
	{
		private static readonly FieldRef<Player, bool> CrouchToggledRef = AccessTools.FieldRefAccess<Player, bool>("m_crouchToggled");

		private static Player trackedPlayer;

		private static bool lastDodgeIntent;

		private static void Postfix(Player __instance, bool blockHold, ref bool jump, ref bool dodge)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: 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)
			if ((Object)(object)__instance != (Object)(object)trackedPlayer)
			{
				trackedPlayer = __instance;
				lastDodgeIntent = false;
			}
			if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || ((Character)__instance).IsDead() || ((Character)__instance).IsSwimming())
			{
				lastDodgeIntent = false;
				return;
			}
			if (StridePlugin.FallRollEnabled == null || !StridePlugin.FallRollEnabled.Value)
			{
				lastDodgeIntent = false;
				return;
			}
			Rigidbody val = PlayerUpdateWallJumpPatch.TryGetBody((Character)(object)__instance);
			string triggerSource;
			if ((Object)(object)val == (Object)null || ((Character)__instance).IsOnGround() || val.linearVelocity.y >= -0.1f)
			{
				lastDodgeIntent = false;
			}
			else if (!TryGetDodgeIntent(__instance, blockHold, jump, dodge, out triggerSource))
			{
				lastDodgeIntent = false;
			}
			else if (!lastDodgeIntent)
			{
				lastDodgeIntent = true;
				StrideState.RecordFallRollAttempt(0f - val.linearVelocity.y);
				LogDebug(StridePlugin.DebugLogFeature.FallRoll, $"Buffered fall roll attempt via {triggerSource} at downward speed {0f - val.linearVelocity.y:0.###}.");
			}
		}

		internal static bool TryGetDodgeIntent(Player player, bool blockHold, bool jump, bool dodge, out string triggerSource)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (dodge)
			{
				triggerSource = "dodge signal";
				return true;
			}
			if ((int)ZInput.InputLayout == 0 || !ZInput.IsGamepadActive())
			{
				bool flag = ((Character)player).IsCrouching() || CrouchToggledRef.Invoke(player);
				if (jump && (blockHold || flag))
				{
					triggerSource = (blockHold ? "jump+block" : "jump+crouch");
					return true;
				}
			}
			triggerSource = string.Empty;
			return false;
		}

		private static void LogDebug(StridePlugin.DebugLogFeature feature, string message)
		{
			if (StridePlugin.IsDebugEnabled(feature))
			{
				StridePlugin.Log.LogInfo((object)message);
			}
		}
	}
	[HarmonyPatch(typeof(SEMan), "ModifyFallDamage")]
	internal static class SEManModifyFallDamagePatch
	{
		private static readonly FieldRef<SEMan, Character> OwnerRef = AccessTools.FieldRefAccess<SEMan, Character>("m_character");

		private static readonly FieldRef<Player, EffectList> PerfectDodgeEffectsRef = AccessTools.FieldRefAccess<Player, EffectList>("m_perfectDodgeEffects");

		private static void Prefix(SEMan __instance, float baseDamage, ref float damage)
		{
			if (StridePlugin.FallRollEnabled != null && StridePlugin.FallRollEnabled.Value)
			{
				Character val = ((__instance != null) ? OwnerRef.Invoke(__instance) : null);
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer))
				{
					LogFallRollResult($"HOOK: baseDamage={baseDamage:0.###} damageBeforeEffects={damage:0.###} hasBufferedAttempt={StrideState.HasPendingFallRollAttemptForDebug()}");
				}
			}
		}

		private static void Postfix(SEMan __instance, float baseDamage, ref float damage)
		{
			//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_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			if (StridePlugin.FallRollEnabled == null || !StridePlugin.FallRollEnabled.Value || __instance == null)
			{
				return;
			}
			Character val = OwnerRef.Invoke(__instance);
			if (!((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && !(damage <= 0f) && StrideState.TryConsumeFallRollDamageReduction(out var reduction, out var bufferedAge, out var downwardSpeed))
			{
				float num = Mathf.Clamp01(reduction);
				float num2 = damage;
				damage *= 1f - num;
				float num3 = Mathf.Max(0f, num2 - damage);
				Player val2 = (Player)(object)((val is Player) ? val : null);
				if (val2 != null)
				{
					PerfectDodgeEffectsRef.Invoke(val2).Create(((Component)val2).transform.position, Quaternion.identity, ((Component)val2).transform, 1f, -1, default(ZDOID));
					((Character)val2).Message((MessageType)2, "Perfect landing", 0, (Sprite)null, false);
				}
				LogFallRollResult($"SUCCESS: bufferedAge={bufferedAge:0.###}s downwardSpeed={downwardSpeed:0.###} baseDamage={baseDamage:0.###} originalDamage={num2:0.###} savedDamage={num3:0.###} finalDamage={damage:0.###} reduction={num * 100f:0.#}%");
				LogDebug(StridePlugin.DebugLogFeature.FallRoll, $"Applied buffered fall roll mitigation. Reduced fall damage by {num * 100f:0.#}% after {bufferedAge:0.###} s buffer at downward speed {downwardSpeed:0.###}.");
			}
		}

		private static void LogDebug(StridePlugin.DebugLogFeature feature, string message)
		{
			if (StridePlugin.IsDebugEnabled(feature))
			{
				StridePlugin.Log.LogInfo((object)message);
			}
		}

		private static void LogFallRollResult(string message)
		{
			if (StridePlugin.IsDebugEnabled(StridePlugin.DebugLogFeature.FallRoll))
			{
				ManualLogSource log = StridePlugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[FallRoll] " + message));
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "HaveStamina")]
	internal static class PlayerHaveStaminaDebtPatch
	{
		private static bool Prefix(Player __instance, float amount, ref bool __result)
		{
			if (!StaminaDebt.ShouldManage(__instance))
			{
				return true;
			}
			if (StaminaDebt.IsInDebt(__instance))
			{
				StaminaDebt.LogBlockedSpend(__instance, amount);
				__result = false;
				return false;
			}
			if (StaminaDebt.CanBorrowForChunkedUse(__instance, amount))
			{
				__result = true;
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Player), "RPC_UseStamina")]
	internal static class PlayerRpcUseStaminaDebtPatch
	{
		private static bool Prefix(Player __instance, float v)
		{
			if (!StaminaDebt.ShouldManage(__instance))
			{
				return true;
			}
			if (!StaminaDebt.IsFinite(v) || v == 0f)
			{
				return false;
			}
			if (StaminaDebt.IsInDebt(__instance))
			{
				StaminaDebt.LogBlockedSpend(__instance, v);
				return false;
			}
			if (!StaminaDebt.CanBorrowForChunkedUse(__instance, v))
			{
				return true;
			}
			StaminaDebt.ApplyDebt(__instance, v);
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateStats", new Type[] { typeof(float) })]
	internal static class PlayerUpdateStatsDebtPatch
	{
		private static void Prefix(Player __instance, out float __state)
		{
			__state = StaminaDebt.GetCurrentStamina(__instance);
			if (StaminaDebt.ShouldManage(__instance) && StaminaDebt.IsInDebt(__instance))
			{
				StaminaDebt.ClearRegenDelay(__instance);
			}
		}

		private static void Postfix(Player __instance, float __state)
		{
			StaminaDebt.LogUpdate(__instance, __state);
		}
	}
	[HarmonyPatch(typeof(Hud), "UpdateStamina")]
	internal static class HudStaminaDebtPatch
	{
		private static void Postfix(Hud __instance, Player player)
		{
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer))
			{
				if (StaminaDebt.IsInDebt(player))
				{
					StaminaDebt.ApplyHudDebtVisuals(__instance, player);
				}
				else
				{
					StaminaDebt.RestoreHudVisuals(__instance);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateFood", new Type[]
	{
		typeof(float),
		typeof(bool)
	})]
	internal static class PlayerUpdateFoodDebtPatch
	{
		private static void Prefix(Player __instance, out float __state)
		{
			__state = (StaminaDebt.ShouldManage(__instance) ? StaminaDebt.GetCurrentStamina(__instance) : 0f);
		}

		private static void Postfix(Player __instance, float __state)
		{
			if (StaminaDebt.ShouldManage(__instance) && !(__state >= 0f))
			{
				StaminaDebt.RestoreDebtAfterFoodUpdateClamp(__instance, __state);
			}
		}
	}
	internal static class StaminaDebt
	{
		private const float MinimumChunkedDebtUse = 5f;

		private const float MaxDebtVisualRatio = 1.25f;

		private static readonly Color DebtBarColor = new Color(1f, 0.2f, 0.2f, 1f);

		private static readonly FieldRef<Character, ZNetView> CharacterNViewRef = AccessTools.FieldRefAccess<Character, ZNetView>("m_nview");

		private static readonly FieldRef<GuiBar, RectTransform> GuiBarRectRef = AccessTools.FieldRefAccess<GuiBar, RectTransform>("m_bar");

		private static readonly FieldRef<GuiBar, float> GuiBarWidthRef = AccessTools.FieldRefAccess<GuiBar, float>("m_width");

		private static readonly FieldRef<Player, float> PlayerStaminaRef = AccessTools.FieldRefAccess<Player, float>("m_stamina");

		private static readonly FieldRef<Player, float> PlayerStaminaRegenTimerRef = AccessTools.FieldRefAccess<Player, float>("m_staminaRegenTimer");

		private static bool wasDebtActiveLastUpdate;

		private static bool wasDebtHudActiveLastUpdate;

		private static int lastLoggedDebtWholeValue = int.MinValue;

		private static int lastLoggedBlockedSpendWholeValue = int.MinValue;

		private static float lastLoggedBlockedSpendAmount = float.NaN;

		internal static void ResetTracking()
		{
			wasDebtActiveLastUpdate = false;
			wasDebtHudActiveLastUpdate = false;
			lastLoggedDebtWholeValue = int.MinValue;
			lastLoggedBlockedSpendWholeValue = int.MinValue;
			lastLoggedBlockedSpendAmount = float.NaN;
		}

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

		internal static bool ShouldManage(Player player)
		{
			if (StridePlugin.StaminaDebtEnabled == null || !StridePlugin.StaminaDebtEnabled.Value)
			{
				return false;
			}
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			ZNetView val = CharacterNViewRef.Invoke((Character)(object)player);
			if ((Object)(object)player == (Object)(object)Player.m_localPlayer && (Object)(object)val != (Object)null && val.IsValid())
			{
				return val.IsOwner();
			}
			return false;
		}

		internal static float GetCurrentStamina(Player player)
		{
			if (!((Object)(object)player != (Object)null))
			{
				return 0f;
			}
			return PlayerStaminaRef.Invoke(player);
		}

		internal static bool IsInDebt(Player player)
		{
			if ((Object)(object)player != (Object)null)
			{
				return PlayerStaminaRef.Invoke(player) < 0f;
			}
			return false;
		}

		internal static bool CanBorrowForChunkedUse(Player player, float amount)
		{
			if ((Object)(object)player == (Object)null || !IsFinite(amount) || amount < 5f)
			{
				return false;
			}
			float num = PlayerStaminaRef.Invoke(player);
			if (num > 0f)
			{
				return amount > num;
			}
			return false;
		}

		internal static void ApplyDebt(Player player, float amount)
		{
			if (!((Object)(object)player == (Object)null) && IsFinite(amount) && !(amount <= 0f))
			{
				float num = PlayerStaminaRef.Invoke(player);
				PlayerStaminaRef.Invoke(player) -= amount;
				PlayerStaminaRegenTimerRef.Invoke(player) = 0f;
				Log($"Borrowed stamina debt. Cost {amount:0.###}, stamina {num:0.###} -> {PlayerStaminaRef.Invoke(player):0.###}.");
			}
		}

		internal static void ClearRegenDelay(Player player)
		{
			PlayerStaminaRegenTimerRef.Invoke(player) = 0f;
		}

		internal static void ApplyHudDebtVisuals(Hud hud, Player player)
		{
			float num = PlayerStaminaRef.Invoke(player);
			float num2 = Mathf.Max(1f, ((Character)player).GetMaxStamina());
			float debtRatio = Mathf.Min(1.25f, (0f - num) / num2);
			hud.m_staminaText.text = StringExtensionMethods.ToFastString(Mathf.CeilToInt(num));
			SetDebtBar(hud.m_staminaBar2Fast, debtRatio);
			SetDebtBar(hud.m_staminaBar2Slow, debtRatio);
			wasDebtHudActiveLastUpdate = true;
		}

		internal static void RestoreHudVisuals(Hud hud)
		{
			if (wasDebtHudActiveLastUpdate)
			{
				RestoreBar(hud.m_staminaBar2Fast);
				RestoreBar(hud.m_staminaBar2Slow);
				wasDebtHudActiveLastUpdate = false;
			}
		}

		private static void SetDebtBar(GuiBar bar, float debtRatio)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)bar == (Object)null))
			{
				if (((Behaviour)bar).enabled)
				{
					((Behaviour)bar).enabled = false;
				}
				bar.SetColor(DebtBarColor);
				RectTransform val = GuiBarRectRef.Invoke(bar);
				if ((Object)(object)val != (Object)null)
				{
					Vector3 localScale = ((Transform)val).localScale;
					localScale.x = 0f - Mathf.Abs(localScale.x);
					((Transform)val).localScale = localScale;
					val.SetSizeWithCurrentAnchors((Axis)0, GuiBarWidthRef.Invoke(bar) * debtRatio);
				}
			}
		}

		private static void RestoreBar(GuiBar bar)
		{
			//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_002f: 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)
			if (!((Object)(object)bar == (Object)null))
			{
				bar.ResetColor();
				RectTransform val = GuiBarRectRef.Invoke(bar);
				if ((Object)(object)val != (Object)null)
				{
					Vector3 localScale = ((Transform)val).localScale;
					localScale.x = Mathf.Abs(localScale.x);
					((Transform)val).localScale = localScale;
				}
				if (!((Behaviour)bar).enabled)
				{
					((Behaviour)bar).enabled = true;
				}
			}
		}

		internal static void LogUpdate(Player player, float previousStamina)
		{
			if (!ShouldManage(player))
			{
				return;
			}
			float num = PlayerStaminaRef.Invoke(player);
			bool num2 = num < 0f;
			if (num2)
			{
				int num3 = Mathf.CeilToInt(num);
				if (!wasDebtActiveLastUpdate)
				{
					Log($"Debt active. Stamina crossed below zero: {previousStamina:0.###} -> {num:0.###}.");
				}
				else if (num3 != lastLoggedDebtWholeValue)
				{
					Log($"Debt repaying. Stamina {previousStamina:0.###} -> {num:0.###}, regen timer {PlayerStaminaRegenTimerRef.Invoke(player):0.###}.");
				}
				lastLoggedDebtWholeValue = num3;
			}
			else if (wasDebtActiveLastUpdate)
			{
				Log($"Debt cleared. Stamina {previousStamina:0.###} -> {num:0.###}.");
				lastLoggedDebtWholeValue = int.MinValue;
				lastLoggedBlockedSpendWholeValue = int.MinValue;
				lastLoggedBlockedSpendAmount = float.NaN;
			}
			wasDebtActiveLastUpdate = num2;
		}

		internal static void LogBlockedSpend(Player player, float amount)
		{
			if (!((Object)(object)player == (Object)null))
			{
				float num = PlayerStaminaRef.Invoke(player);
				int num2 = Mathf.CeilToInt(num);
				if (num2 != lastLoggedBlockedSpendWholeValue || !(Mathf.Abs(amount - lastLoggedBlockedSpendAmount) < 0.01f))
				{
					lastLoggedBlockedSpendWholeValue = num2;
					lastLoggedBlockedSpendAmount = amount;
					Log($"Blocked stamina spend while in debt. Cost {amount:0.###}, current stamina {num:0.###}.");
				}
			}
		}

		internal static void RestoreDebtAfterFoodUpdateClamp(Player player, float debtStamina)
		{
			float num = PlayerStaminaRef.Invoke(player);
			if (num == 0f)
			{
				PlayerStaminaRef.Invoke(player) = debtStamina;
				Log($"Restored stamina debt after food update clamp. Stamina {num:0.###} -> {debtStamina:0.###}.");
			}
		}

		private static void Log(string message)
		{
			if (StridePlugin.IsDebugEnabled(StridePlugin.DebugLogFeature.StaminaDebt))
			{
				ManualLogSource log = StridePlugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[StaminaDebt] " + message));
				}
			}
		}
	}
	internal static class StrideState
	{
		internal struct WallContactSnapshot
		{
			internal Vector3 Normal;

			internal Vector3 ApproachVelocity;

			internal float ContactTime;

			internal float ApproachTime;

			internal float NearestDistance;

			internal static WallContactSnapshot Invalid => new WallContactSnapshot
			{
				Normal = Vector3.zero,
				ApproachVelocity = Vector3.zero,
				ContactTime = -999f,
				ApproachTime = -999f,
				NearestDistance = float.MaxValue
			};
		}

		private static float lastGroundedTime = -999f;

		private static float lastGroundJumpTime = -999f;

		private static float lastJumpPressedTime = -999f;

		private static float lastWallJumpTime = -999f;

		private static float lastFallRollAttemptTime = -999f;

		private static float lastFallRollDownwardSpeed;

		private static int lastResolvedJumpPressFrame = -1;

		private static int lastSuccessfulJumpFrame = -999;

		private static bool hasResolvedJumpPressQueued;

		private static bool suppressNextGroundJumpLog;

		private static bool usedGroundJumpSinceLastGrounded;

		private static bool usedWallJumpThisAirborneState;

		private static WallContactSnapshot lastWallContact = WallContactSnapshot.Invalid;

		internal static void RecordResolvedJumpPress()
		{
			if (lastResolvedJumpPressFrame != Time.frameCount)
			{
				lastResolvedJumpPressFrame = Time.frameCount;
				hasResolvedJumpPressQueued = true;
			}
		}

		internal static bool TryConsumeResolvedJumpPress()
		{
			if (!hasResolvedJumpPressQueued)
			{
				return false;
			}
			hasResolvedJumpPressQueued = false;
			return true;
		}

		private static void ClearResolvedJumpPress()
		{
			hasResolvedJumpPressQueued = false;
		}

		internal static void RecordJumpPressed()
		{
			if (UsesBufferedJumpTracking())
			{
				lastJumpPressedTime = Time.time;
			}
		}

		internal static void RecordGrounded()
		{
			lastGroundedTime = Time.time;
			usedGroundJumpSinceLastGrounded = false;
			lastWallContact = WallContactSnapshot.Invalid;
		}

		internal static void RecordGroundJump()
		{
			ClearResolvedJumpPress();
			lastSuccessfulJumpFrame = Time.frameCount;
			lastGroundJumpTime = Time.time;
			usedGroundJumpSinceLastGrounded = true;
			lastJumpPressedTime = -999f;
			usedWallJumpThisAirborneState = false;
			lastWallContact = WallContactSnapshot.Invalid;
			if (suppressNextGroundJumpLog)
			{
				suppressNextGroundJumpLog = false;
			}
			else
			{
				LogJumpTiming("Recorded regular jump.");
			}
		}

		internal static void RecordWallJump()
		{
			ClearResolvedJumpPress();
			lastSuccessfulJumpFrame = Time.frameCount;
			lastWallJumpTime = Time.time;
			lastJumpPressedTime = -999f;
			usedWallJumpThisAirborneState = true;
			lastWa