Decompiled source of ConcernedLadders v1.0.0

plugins/TheConcernedCat.ConcernedLadders.dll

Decompiled 13 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using TheConcernedCat.ConcernedLadders.Domain;
using TheConcernedCat.ConcernedLadders.Domain.Animation;
using TheConcernedCat.ConcernedLadders.Pieces;
using TheConcernedCat.ConcernedLadders.Runtime;
using TheConcernedCat.ConcernedLadders.Runtime.Animation;
using TheConcernedCat.Diagnostics;
using TheConcernedCat.Ladders;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("The Concerned Cat")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © 2026 Eren Cansunar")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+4cb5e566ea8878970be7135d6319de5e7ee1fe14")]
[assembly: AssemblyProduct("Concerned Ladders")]
[assembly: AssemblyTitle("TheConcernedCat.ConcernedLadders")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/Weakened/ConcernedCatMods")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace TheConcernedCat.Diagnostics
{
	internal static class PathScrubber
	{
		public const int DefaultMaxLength = 2000;

		private const string WindowsSegmentChar = "[^\\\\/\\r\\n:*?\"<>|\\s]";

		private const string UnixSegmentChar = "[^/\\s]";

		private const string UnixRunChar = "[^/<>\\s]";

		private const string WindowsFinalRunChar = "[^\\\\/\\r\\n:*?\"<>|\\s.,;]";

		private const string UnixFinalRunChar = "[^/<>\\r\\n\\s.,;]";

		private const string TokenCap = "{0,2}";

		private const string WindowsChainRun = "(?: [^\\\\/\\r\\n:*?\"<>|\\s]+){0,2}";

		private const string UnixChainRun = "(?: [^/<>\\s]+){0,2}";

		private const string WindowsFinalRun = "(?: [^\\\\/\\r\\n:*?\"<>|\\s.,;]+){0,2}";

		private const string UnixFinalRun = "(?: [^/<>\\r\\n\\s.,;]+){0,2}";

		private const string NoFileExtension = "(?<!\\.[A-Za-z0-9]{1,8})";

		private const string PathEnd = "(?=$|[\\r\\n'\"`,;])";

		private static readonly Regex Urls = new Regex("\\b(?:https?|wss?|ftp)://\\S+", RegexOptions.Compiled);

		private static readonly Regex CoordinatePairs = new Regex("\\(\\s*-?\\d+(?:\\.\\d+)?\\s*,\\s*-?\\d+(?:\\.\\d+)?(?:\\s*,\\s*-?\\d+(?:\\.\\d+)?)?\\s*\\)", RegexOptions.Compiled);

		private static readonly Regex WindowsPath = new Regex("[A-Za-z]:[\\\\/](?:[^\\\\/\\r\\n:*?\"<>|\\s]+(?: [^\\\\/\\r\\n:*?\"<>|\\s]+){0,2}[\\\\/])*([^\\\\/\\r\\n:*?\"<>|\\s]*)(?:(?<!\\.[A-Za-z0-9]{1,8})(?: [^\\\\/\\r\\n:*?\"<>|\\s.,;]+){0,2}(?=$|[\\r\\n'\"`,;]))?", RegexOptions.Compiled);

		private static readonly Regex WindowsUncPath = new Regex("\\\\\\\\(?:[^\\\\/\\r\\n:*?\"<>|\\s]+(?: [^\\\\/\\r\\n:*?\"<>|\\s]+){0,2}\\\\){1,}([^\\\\/\\r\\n:*?\"<>|\\s]*)(?:(?<!\\.[A-Za-z0-9]{1,8})(?: [^\\\\/\\r\\n:*?\"<>|\\s.,;]+){0,2}(?=$|[\\r\\n'\"`,;]))?", RegexOptions.Compiled);

		private static readonly Regex WindowsRelativePath = new Regex("(?<![\\\\/\\w.<>])[^\\\\/\\r\\n:*?\"<>|\\s]+\\\\(?:[^\\\\/\\r\\n:*?\"<>|\\s]+(?: [^\\\\/\\r\\n:*?\"<>|\\s]+){0,2}\\\\){1,}([^\\\\/\\r\\n:*?\"<>|\\s]*)(?:(?<!\\.[A-Za-z0-9]{1,8})(?: [^\\\\/\\r\\n:*?\"<>|\\s.,;]+){0,2}(?=$|[\\r\\n'\"`,;]))?", RegexOptions.Compiled);

		private static readonly Regex FileThenProseBeforeRelative = new Regex("\\.[A-Za-z0-9]{1,8}(?: [^\\\\/\\r\\n:*?\"<>|\\s]+)+ $", RegexOptions.Compiled);

		private static readonly Regex UnixPath = new Regex("(?<![\\w.<>])/(?:[^/\\s]+(?: [^/<>\\s]+){0,2}/)+([^/\\s]*)(?:(?<!\\.[A-Za-z0-9]{1,8})(?: [^/<>\\r\\n\\s.,;]+){0,2}(?=$|[\\r\\n'\"`,;]))?", RegexOptions.Compiled);

		private static readonly Regex UsersFragment = new Regex("\\bUsers[\\\\/][^\\\\/\\s]+", RegexOptions.IgnoreCase | RegexOptions.Compiled);

		private static readonly Regex SaveFileNames = new Regex("[^\\\\/\\s\"']+\\.(db|fwl|fch)(\\.old|\\.bak)?\\b", RegexOptions.IgnoreCase | RegexOptions.Compiled);

		private static readonly Regex Ipv4 = new Regex("\\b\\d{1,3}(?:\\.\\d{1,3}){3}(?::\\d{1,5})?\\b", RegexOptions.Compiled);

		private static readonly Regex HexBlob = new Regex("\\b[A-Fa-f0-9]{32,}\\b", RegexOptions.Compiled);

		private static readonly Regex TokenBlob = new Regex("[A-Za-z0-9+/=_\\-]{40,}", RegexOptions.Compiled);

		private static readonly Regex LongDigits = new Regex("\\d{7,}", RegexOptions.Compiled);

		private static void CreateMarkerTokens(string text, out string cappedContinuation, out string rootedPathStart, out string rootedPathEnd)
		{
			string text2 = "<";
			while (text.IndexOf(text2, StringComparison.Ordinal) >= 0)
			{
				text2 += "<";
			}
			cappedContinuation = text2 + "|";
			rootedPathStart = text2 + ":";
			rootedPathEnd = text2 + "?";
		}

		private static bool IsWindowsSegmentCharacter(char value)
		{
			if (!char.IsWhiteSpace(value))
			{
				return "\\/\r\n:*?\"<>|".IndexOf(value) < 0;
			}
			return false;
		}

		private static bool HasCappedContinuation(string text, int index)
		{
			int num = 0;
			while (index < text.Length && text[index] == ' ')
			{
				index++;
				int num2 = index;
				while (index < text.Length && IsWindowsSegmentCharacter(text[index]))
				{
					index++;
				}
				if (index == num2)
				{
					return false;
				}
				num++;
				if (index < text.Length && text[index] == '\\')
				{
					return num >= 3;
				}
			}
			return false;
		}

		private static string MarkRootedWindowsPaths(Regex pattern, string text, string cappedContinuation, string rootedPathStart, string rootedPathEnd)
		{
			return pattern.Replace(text, delegate(Match match)
			{
				foreach (Match item in WindowsRelativePath.Matches(match.Value))
				{
					if (item.Index > 0 && FileThenProseBeforeRelative.IsMatch(match.Value.Substring(0, item.Index)))
					{
						return rootedPathStart + match.Value.Substring(0, item.Index) + rootedPathEnd + match.Value.Substring(item.Index);
					}
				}
				return rootedPathStart + match.Value + rootedPathEnd + (HasCappedContinuation(text, match.Index + match.Length) ? cappedContinuation : "");
			});
		}

		private static bool IsCappedContinuationGap(string text, int marker, int markerLength, int matchIndex)
		{
			for (int i = marker + markerLength; i < matchIndex; i++)
			{
				char c = text[i];
				if (c != ' ' && !IsWindowsSegmentCharacter(c))
				{
					return false;
				}
			}
			return matchIndex > marker + markerLength;
		}

		private static string ReplaceRelativeWindowsPaths(string text, string replacement, string cappedContinuation, string rootedPathStart, string rootedPathEnd)
		{
			int usedMarker = -1;
			return WindowsRelativePath.Replace(text, delegate(Match match)
			{
				int num = match.Index;
				while (num > 0 && text[num - 1] != '\r' && text[num - 1] != '\n')
				{
					num--;
				}
				int num2 = ((match.Index > num) ? text.LastIndexOf(rootedPathStart, match.Index - 1, match.Index - num, StringComparison.Ordinal) : (-1));
				int num3 = ((match.Index > num) ? text.LastIndexOf(rootedPathEnd, match.Index - 1, match.Index - num, StringComparison.Ordinal) : (-1));
				if (num2 > num3)
				{
					return match.Value;
				}
				int num4 = ((match.Index > num) ? text.LastIndexOf(cappedContinuation, match.Index - 1, match.Index - num, StringComparison.Ordinal) : (-1));
				if (num4 >= num && num4 != usedMarker && IsCappedContinuationGap(text, num4, cappedContinuation.Length, match.Index))
				{
					usedMarker = num4;
					return match.Value;
				}
				return match.Result(replacement);
			}).Replace(cappedContinuation, "").Replace(rootedPathStart, "")
				.Replace(rootedPathEnd, "");
		}

		public static string Scrub(string? text, bool keepFileName, int maxLength = 2000)
		{
			if (string.IsNullOrEmpty(text))
			{
				return "";
			}
			string text2 = (keepFileName ? "<path>/$1" : "<path>");
			string replacement = text2;
			string input = text;
			input = Urls.Replace(input, "<url>");
			input = CoordinatePairs.Replace(input, "(<pos>)");
			CreateMarkerTokens(input, out string cappedContinuation, out string rootedPathStart, out string rootedPathEnd);
			input = MarkRootedWindowsPaths(WindowsPath, input, cappedContinuation, rootedPathStart, rootedPathEnd);
			input = MarkRootedWindowsPaths(WindowsUncPath, input, cappedContinuation, rootedPathStart, rootedPathEnd);
			input = ReplaceRelativeWindowsPaths(input, text2, cappedContinuation, rootedPathStart, rootedPathEnd);
			input = WindowsPath.Replace(input, text2);
			input = WindowsUncPath.Replace(input, text2);
			input = UnixPath.Replace(input, replacement);
			input = UsersFragment.Replace(input, "Users/<user>");
			input = SaveFileNames.Replace(input, "<save>.$1");
			input = Ipv4.Replace(input, "<ip>");
			input = HexBlob.Replace(input, "<hex>");
			input = TokenBlob.Replace(input, "<token>");
			input = LongDigits.Replace(input, "<n>");
			if (input.Length > maxLength)
			{
				input = input.Substring(0, maxLength) + "…[truncated]";
			}
			return input;
		}
	}
	internal static class SafeFailure
	{
		public static string Describe(string command, string subcommand, Exception? exception)
		{
			string text = (string.IsNullOrWhiteSpace(subcommand) ? command : (command + " " + subcommand));
			string text2 = Brief(exception);
			if (!string.IsNullOrEmpty(text2))
			{
				return text + " could not finish: " + text2;
			}
			return text + " could not finish.";
		}

		public static string Brief(Exception? exception)
		{
			if (exception == null)
			{
				return "";
			}
			string text = PathScrubber.Scrub(exception.Message, keepFileName: false);
			if (!string.IsNullOrEmpty(text))
			{
				return exception.GetType().Name + ": " + text;
			}
			return exception.GetType().Name;
		}

		public static string Describe(Exception? exception)
		{
			if (exception != null)
			{
				return PathScrubber.Scrub(exception.ToString(), keepFileName: false, 8000);
			}
			return "";
		}
	}
}
namespace TheConcernedCat.Ladders
{
	internal enum ClimbExitKind
	{
		Unspecified,
		Bottom,
		Top,
		JumpedOff,
		LetGo
	}
	internal readonly struct ClimbExitPlan
	{
		public ClimbExitKind Kind { get; }

		public ClimbPoint Landing { get; }

		public ClimbHeading Facing { get; }

		public bool PlaceCharacter { get; }

		private ClimbExitPlan(ClimbExitKind kind, ClimbPoint landing, ClimbHeading facing, bool placeCharacter)
		{
			Kind = kind;
			Landing = landing;
			Facing = facing;
			PlaceCharacter = placeCharacter;
		}

		public static ClimbExitPlan Step(ClimbExitKind kind, ClimbPoint landing, ClimbHeading facing)
		{
			if (kind != ClimbExitKind.Bottom && kind != ClimbExitKind.Top)
			{
				throw new ArgumentOutOfRangeException("kind", "Only the two step-offs place a character.");
			}
			return new ClimbExitPlan(kind, landing, facing, placeCharacter: true);
		}

		public static ClimbExitPlan Release(ClimbExitKind kind)
		{
			return new ClimbExitPlan(kind, default(ClimbPoint), ClimbHeading.None, placeCharacter: false);
		}
	}
	internal readonly struct TopLanding
	{
		public bool HasStandingSpace { get; }

		public ClimbPoint Surface { get; }

		public static TopLanding None => new TopLanding(hasStandingSpace: false, default(ClimbPoint));

		public TopLanding(bool hasStandingSpace, ClimbPoint surface)
		{
			HasStandingSpace = hasStandingSpace;
			Surface = surface;
		}
	}
	internal static class ClimbExit
	{
		public static ClimbExitPlan? OverTheTop(in LadderGeometry ladder, in TopLanding landing, ClimbLimits limits)
		{
			if (limits == null)
			{
				throw new ArgumentNullException("limits");
			}
			if (!landing.HasStandingSpace || !landing.Surface.IsFinite)
			{
				return null;
			}
			ClimbHeading facingWhileClimbing = ladder.FacingWhileClimbing;
			ClimbPoint landing2 = landing.Surface.Offset(facingWhileClimbing, limits.TopStepInMetres).WithHeight(landing.Surface.Y + limits.TopStepUpMetres);
			return ClimbExitPlan.Step(ClimbExitKind.Top, landing2, facingWhileClimbing);
		}

		public static ClimbExitPlan OffTheBottom(in LadderGeometry ladder, ClimbLimits limits)
		{
			if (limits == null)
			{
				throw new ArgumentNullException("limits");
			}
			ClimbPoint landing = ladder.Bottom.Offset(ladder.StandingSide, limits.BodyOffsetMetres);
			return ClimbExitPlan.Step(ClimbExitKind.Bottom, landing, ladder.StandingSide);
		}

		public static ClimbExitPlan LetGo(bool deliberate)
		{
			return ClimbExitPlan.Release(deliberate ? ClimbExitKind.JumpedOff : ClimbExitKind.LetGo);
		}
	}
	internal sealed class ClimbLimits
	{
		public float MountReachMetres { get; set; } = 1.1f;

		public float MountSideMarginMetres { get; set; } = 0.45f;

		public float MountHeightMarginMetres { get; set; } = 1.2f;

		public float MountFacingAgreement { get; set; } = 0.35f;

		public float ClimbSpeedMetresPerSecond { get; set; } = 1.8f;

		public float AlignSeconds { get; set; } = 0.25f;

		public float BodyOffsetMetres { get; set; } = 0.35f;

		public float TopStepUpMetres { get; set; } = 0.4f;

		public float TopStepInMetres { get; set; } = 0.6f;

		public float LostContactMetres { get; set; } = 1.6f;

		public float StaminaPerSecond { get; set; }

		public float SpeedMultiplier { get; set; } = 1f;

		public float EffectiveClimbSpeed => Math.Max(0.1f, ClimbSpeedMetresPerSecond * SpeedMultiplier);

		public static ClimbLimits Default => new ClimbLimits();

		public ClimbLimits Validate()
		{
			Require(MountReachMetres > 0f && MountReachMetres <= 3f, "MountReachMetres");
			Require(MountSideMarginMetres >= 0f && MountSideMarginMetres <= 2f, "MountSideMarginMetres");
			Require(MountHeightMarginMetres >= 0f && MountHeightMarginMetres <= 3f, "MountHeightMarginMetres");
			Require(MountFacingAgreement >= -1f && MountFacingAgreement <= 1f, "MountFacingAgreement");
			Require(ClimbSpeedMetresPerSecond > 0f && ClimbSpeedMetresPerSecond <= 6f, "ClimbSpeedMetresPerSecond");
			Require(AlignSeconds >= 0f && AlignSeconds <= 1f, "AlignSeconds");
			Require(BodyOffsetMetres > 0f && BodyOffsetMetres <= 1f, "BodyOffsetMetres");
			Require(TopStepUpMetres >= 0f && TopStepUpMetres <= 2f, "TopStepUpMetres");
			Require(TopStepInMetres > 0f && TopStepInMetres <= 2f, "TopStepInMetres");
			Require(LostContactMetres > BodyOffsetMetres, "LostContactMetres");
			Require(StaminaPerSecond >= 0f && StaminaPerSecond <= 10f, "StaminaPerSecond");
			Require(SpeedMultiplier >= 0.25f && SpeedMultiplier <= 3f, "SpeedMultiplier");
			return this;
		}

		private static void Require(bool condition, string name)
		{
			if (!condition)
			{
				throw new ArgumentOutOfRangeException(name, "A climb limit is outside its designed range.");
			}
		}
	}
	internal sealed class ClimbOptions
	{
		public bool Enabled { get; set; } = true;

		public bool AutoMount { get; set; } = true;

		public float ClimbSpeedMultiplier { get; set; } = 1f;

		public float StaminaPerSecond { get; set; }

		public float ReMountCooldownSeconds { get; set; } = 0.35f;

		public float SurveyRadiusMetres { get; set; } = 4f;

		public float SurveyIntervalSeconds { get; set; } = 0.25f;

		public float TopProbeWithinMetres { get; set; } = 0.75f;

		public float TopFloorAboveMetres { get; set; } = 1f;

		public float TopFloorBelowMetres { get; set; } = 1.25f;

		public float MaxCorrectionSpeed { get; set; } = 4f;

		public ClimbLimits ToLimits()
		{
			return new ClimbLimits
			{
				SpeedMultiplier = Clamp(ClimbSpeedMultiplier, 0.25f, 3f, 1f),
				StaminaPerSecond = Clamp(StaminaPerSecond, 0f, 10f, 0f)
			}.Validate();
		}

		private static float Clamp(float value, float low, float high, float fallback)
		{
			if (float.IsNaN(value) || float.IsInfinity(value))
			{
				return fallback;
			}
			return Math.Min(Math.Max(value, low), high);
		}
	}
	internal readonly struct ClimbPoint : IEquatable<ClimbPoint>
	{
		public float X { get; }

		public float Y { get; }

		public float Z { get; }

		public bool IsFinite
		{
			get
			{
				if (IsFiniteValue(X) && IsFiniteValue(Y))
				{
					return IsFiniteValue(Z);
				}
				return false;
			}
		}

		public ClimbPoint(float x, float y, float z)
		{
			X = x;
			Y = y;
			Z = z;
		}

		public float HorizontalDistanceTo(ClimbPoint other)
		{
			float num = X - other.X;
			float num2 = Z - other.Z;
			return (float)Math.Sqrt(num * num + num2 * num2);
		}

		public float VerticalDistanceTo(ClimbPoint other)
		{
			return Math.Abs(Y - other.Y);
		}

		public ClimbPoint WithHeight(float y)
		{
			return new ClimbPoint(X, y, Z);
		}

		public ClimbPoint Offset(ClimbHeading heading, float metres)
		{
			return new ClimbPoint(X + heading.X * metres, Y, Z + heading.Z * metres);
		}

		public ClimbPoint Raise(float metres)
		{
			return new ClimbPoint(X, Y + metres, Z);
		}

		public bool Equals(ClimbPoint other)
		{
			if (X.Equals(other.X) && Y.Equals(other.Y))
			{
				return Z.Equals(other.Z);
			}
			return false;
		}

		public override bool Equals(object? obj)
		{
			if (obj is ClimbPoint other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return ((17 * 31 + X.GetHashCode()) * 31 + Y.GetHashCode()) * 31 + Z.GetHashCode();
		}

		public override string ToString()
		{
			return string.Format(CultureInfo.InvariantCulture, "({0:0.##}, {1:0.##}, {2:0.##})", X, Y, Z);
		}

		private static bool IsFiniteValue(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
	internal readonly struct ClimbHeading : IEquatable<ClimbHeading>
	{
		public static ClimbHeading None => default(ClimbHeading);

		public float X { get; }

		public float Z { get; }

		public bool IsKnown => X * X + Z * Z > 0f;

		private ClimbHeading(float x, float z)
		{
			X = x;
			Z = z;
		}

		public static ClimbHeading FromXz(float x, float z)
		{
			float num = (float)Math.Sqrt(x * x + z * z);
			if (num <= 0.0001f || float.IsNaN(num) || float.IsInfinity(num))
			{
				return None;
			}
			return new ClimbHeading(x / num, z / num);
		}

		public static ClimbHeading FromTo(ClimbPoint from, ClimbPoint to)
		{
			return FromXz(to.X - from.X, to.Z - from.Z);
		}

		public ClimbHeading Opposite()
		{
			if (!IsKnown)
			{
				return None;
			}
			return new ClimbHeading(0f - X, 0f - Z);
		}

		public float Agreement(ClimbHeading other)
		{
			if (!IsKnown || !other.IsKnown)
			{
				return 0f;
			}
			return X * other.X + Z * other.Z;
		}

		public bool Equals(ClimbHeading other)
		{
			if (X.Equals(other.X))
			{
				return Z.Equals(other.Z);
			}
			return false;
		}

		public override bool Equals(object? obj)
		{
			if (obj is ClimbHeading other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return X.GetHashCode() * 31 + Z.GetHashCode();
		}

		public override string ToString()
		{
			if (!IsKnown)
			{
				return "(none)";
			}
			return string.Format(CultureInfo.InvariantCulture, "({0:0.##}, {1:0.##})", X, Z);
		}
	}
	internal static class ClimbPresentation
	{
		public const float MinimumContactIntervalSeconds = 0.22f;

		public static float PoseBlend(in ClimbTelemetry telemetry)
		{
			if (!telemetry.IsClimbing)
			{
				return 0f;
			}
			float alignFraction = telemetry.AlignFraction;
			if (float.IsNaN(alignFraction) || float.IsInfinity(alignFraction))
			{
				return 0f;
			}
			return Math.Min(Math.Max(alignFraction, 0f), 1f);
		}

		public static float AnimatorForwardSpeed(in ClimbTelemetry telemetry)
		{
			if (!telemetry.IsClimbing)
			{
				return 0f;
			}
			float velocity = telemetry.Velocity;
			if (!float.IsNaN(velocity) && !float.IsInfinity(velocity))
			{
				return velocity;
			}
			return 0f;
		}

		public static bool ShouldPlayRungContact(int previousRung, int currentRung, float velocity, float secondsSinceLast)
		{
			if (previousRung < 0 || currentRung < 0 || previousRung == currentRung)
			{
				return false;
			}
			if (float.IsNaN(velocity) || float.IsInfinity(velocity) || Math.Abs(velocity) < 0.01f)
			{
				return false;
			}
			if (!float.IsNaN(secondsSinceLast))
			{
				return secondsSinceLast >= 0.22f;
			}
			return false;
		}
	}
	internal enum ClimbEndReason
	{
		Unspecified,
		LadderDestroyed,
		LadderUnloaded,
		LadderMoved,
		LostContact,
		CharacterDied,
		CharacterTaken,
		WorldUnloading,
		RuntimeStopped,
		ControllerFault
	}
	internal readonly struct ClimbConditions
	{
		public bool LadderAlive { get; }

		public bool LadderLoaded { get; }

		public bool LadderStill { get; }

		public bool CharacterAlive { get; }

		public bool CharacterFree { get; }

		public bool WorldUp { get; }

		public bool RuntimeRunning { get; }

		public float DistanceFromLadder { get; }

		public ClimbConditions(bool ladderAlive, bool ladderLoaded, bool ladderStill, bool characterAlive, bool characterFree, bool worldUp, bool runtimeRunning, float distanceFromLadder)
		{
			LadderAlive = ladderAlive;
			LadderLoaded = ladderLoaded;
			LadderStill = ladderStill;
			CharacterAlive = characterAlive;
			CharacterFree = characterFree;
			WorldUp = worldUp;
			RuntimeRunning = runtimeRunning;
			DistanceFromLadder = distanceFromLadder;
		}

		public static ClimbConditions Fine(float distanceFromLadder = 0f)
		{
			return new ClimbConditions(ladderAlive: true, ladderLoaded: true, ladderStill: true, characterAlive: true, characterFree: true, worldUp: true, runtimeRunning: true, distanceFromLadder);
		}
	}
	internal static class ClimbSafety
	{
		public static ClimbRestoration Restoration => new ClimbRestoration(restoreGravity: true, restoreMotor: true, restoreCollisions: true, clearClimbPose: true, stopClimbSound: true);

		public static ClimbEndReason MustEnd(in ClimbConditions conditions, ClimbLimits limits)
		{
			if (limits == null)
			{
				throw new ArgumentNullException("limits");
			}
			if (!conditions.WorldUp)
			{
				return ClimbEndReason.WorldUnloading;
			}
			if (!conditions.RuntimeRunning)
			{
				return ClimbEndReason.RuntimeStopped;
			}
			if (!conditions.CharacterAlive)
			{
				return ClimbEndReason.CharacterDied;
			}
			if (!conditions.CharacterFree)
			{
				return ClimbEndReason.CharacterTaken;
			}
			if (!conditions.LadderAlive)
			{
				return ClimbEndReason.LadderDestroyed;
			}
			if (!conditions.LadderLoaded)
			{
				return ClimbEndReason.LadderUnloaded;
			}
			if (!conditions.LadderStill)
			{
				return ClimbEndReason.LadderMoved;
			}
			if (conditions.DistanceFromLadder > limits.LostContactMetres)
			{
				return ClimbEndReason.LostContact;
			}
			return ClimbEndReason.Unspecified;
		}

		public static ClimbExitPlan Release(ClimbEndReason reason)
		{
			if (reason == ClimbEndReason.Unspecified)
			{
				throw new ArgumentOutOfRangeException("reason", "A climb does not end without a reason.");
			}
			return ClimbExit.LetGo(deliberate: false);
		}

		public static string Explain(ClimbEndReason reason)
		{
			return reason switch
			{
				ClimbEndReason.LadderDestroyed => "The ladder broke.", 
				ClimbEndReason.LadderUnloaded => "The ladder is too far away now.", 
				ClimbEndReason.LadderMoved => "The ladder moved.", 
				ClimbEndReason.LostContact => "You lost your grip.", 
				ClimbEndReason.CharacterDied => "You died on the ladder.", 
				ClimbEndReason.CharacterTaken => "Something else took hold of you.", 
				ClimbEndReason.WorldUnloading => "The world is closing.", 
				ClimbEndReason.RuntimeStopped => "Ladder climbing stopped.", 
				ClimbEndReason.ControllerFault => "Climbing failed, and you were let go safely.", 
				_ => "The climb ended.", 
			};
		}
	}
	internal readonly struct ClimbRestoration
	{
		public bool RestoreGravity { get; }

		public bool RestoreMotor { get; }

		public bool RestoreCollisions { get; }

		public bool ClearClimbPose { get; }

		public bool StopClimbSound { get; }

		public bool IsComplete
		{
			get
			{
				if (RestoreGravity && RestoreMotor && RestoreCollisions && ClearClimbPose)
				{
					return StopClimbSound;
				}
				return false;
			}
		}

		public ClimbRestoration(bool restoreGravity, bool restoreMotor, bool restoreCollisions, bool clearClimbPose, bool stopClimbSound)
		{
			RestoreGravity = restoreGravity;
			RestoreMotor = restoreMotor;
			RestoreCollisions = restoreCollisions;
			ClearClimbPose = clearClimbPose;
			StopClimbSound = stopClimbSound;
		}
	}
	internal enum ClimbPhase
	{
		None,
		Aligning,
		OnTheLadder,
		Ended
	}
	internal readonly struct ClimbTelemetry
	{
		public ClimbPhase Phase { get; }

		public ClimbMotion Motion { get; }

		public float Progress { get; }

		public float Height { get; }

		public float Velocity { get; }

		public float AnimationSpeed { get; }

		public int Rung { get; }

		public float AlignFraction { get; }

		public ClimbHeading Facing { get; }

		public static ClimbTelemetry Idle => default(ClimbTelemetry);

		public bool IsClimbing
		{
			get
			{
				if (Phase != ClimbPhase.Aligning)
				{
					return Phase == ClimbPhase.OnTheLadder;
				}
				return true;
			}
		}

		internal ClimbTelemetry(ClimbPhase phase, ClimbMotion motion, float progress, float height, float velocity, float animationSpeed, int rung, float alignFraction, ClimbHeading facing)
		{
			Phase = phase;
			Motion = motion;
			Progress = progress;
			Height = height;
			Velocity = velocity;
			AnimationSpeed = animationSpeed;
			Rung = rung;
			AlignFraction = alignFraction;
			Facing = facing;
		}
	}
	internal readonly struct ClimbFrame
	{
		public float DeltaSeconds { get; }

		public ClimbConditions Conditions { get; }

		public float ClimbInput { get; }

		public bool LetGoRequested { get; }

		public TopLanding TopLanding { get; }

		public ClimbFrame(float deltaSeconds, in ClimbConditions conditions, float climbInput, bool letGoRequested, in TopLanding topLanding)
		{
			DeltaSeconds = deltaSeconds;
			Conditions = conditions;
			ClimbInput = climbInput;
			LetGoRequested = letGoRequested;
			TopLanding = topLanding;
		}
	}
	internal readonly struct ClimbStep
	{
		public bool Continues { get; }

		public ClimbPoint BodyTarget { get; }

		public ClimbHeading BodyFacing { get; }

		public float StaminaUsed { get; }

		public bool HasExit { get; }

		public ClimbExitPlan Exit { get; }

		public ClimbEndReason EndReason { get; }

		public ClimbRestoration Restoration { get; }

		public ClimbTelemetry Telemetry { get; }

		private ClimbStep(bool continues, ClimbPoint bodyTarget, ClimbHeading bodyFacing, float staminaUsed, bool hasExit, ClimbExitPlan exit, ClimbEndReason endReason, ClimbRestoration restoration, ClimbTelemetry telemetry)
		{
			Continues = continues;
			BodyTarget = bodyTarget;
			BodyFacing = bodyFacing;
			StaminaUsed = staminaUsed;
			HasExit = hasExit;
			Exit = exit;
			EndReason = endReason;
			Restoration = restoration;
			Telemetry = telemetry;
		}

		internal static ClimbStep Continue(ClimbPoint bodyTarget, ClimbHeading facing, float staminaUsed, ClimbTelemetry telemetry)
		{
			return new ClimbStep(continues: true, bodyTarget, facing, staminaUsed, hasExit: false, default(ClimbExitPlan), ClimbEndReason.Unspecified, default(ClimbRestoration), telemetry);
		}

		internal static ClimbStep Ending(ClimbExitPlan exit, ClimbEndReason reason)
		{
			return new ClimbStep(continues: false, default(ClimbPoint), ClimbHeading.None, 0f, hasExit: true, exit, reason, ClimbSafety.Restoration, ClimbTelemetry.Idle);
		}

		internal static ClimbStep AlreadyOver(ClimbEndReason reason)
		{
			return new ClimbStep(continues: false, default(ClimbPoint), ClimbHeading.None, 0f, hasExit: false, default(ClimbExitPlan), reason, ClimbSafety.Restoration, ClimbTelemetry.Idle);
		}
	}
	internal sealed class MountGate
	{
		private float _cooldown;

		private bool _mustLeaveTheBand;

		public bool AllowsAutoMount
		{
			get
			{
				if (_cooldown <= 0f)
				{
					return !_mustLeaveTheBand;
				}
				return false;
			}
		}

		public bool AllowsDeliberateMount => _cooldown <= 0f;

		public void Tick(float deltaSeconds)
		{
			if (!float.IsNaN(deltaSeconds) && !(deltaSeconds <= 0f) && _cooldown > 0f)
			{
				_cooldown = Math.Max(0f, _cooldown - deltaSeconds);
			}
		}

		public void ClimbEnded(ClimbExitKind kind, float cooldownSeconds)
		{
			_cooldown = Math.Max(0f, cooldownSeconds);
			_mustLeaveTheBand = kind == ClimbExitKind.JumpedOff || kind == ClimbExitKind.LetGo;
		}

		public void OutOfTheBand()
		{
			_mustLeaveTheBand = false;
		}

		public static bool LeavesTheBand(MountRefusal refusal)
		{
			if ((uint)refusal <= 5u)
			{
				return true;
			}
			return false;
		}

		public void Reset()
		{
			_cooldown = 0f;
			_mustLeaveTheBand = false;
		}
	}
	internal sealed class ClimbSession
	{
		private readonly LadderRun _run;

		private readonly ClimbLimits _limits;

		private readonly ClimbTrack _track;

		private readonly ClimbPoint _mountedFrom;

		private readonly ClimbHeading _facedFrom;

		private float _aligned;

		public ClimbPhase Phase { get; private set; }

		public ClimbEndReason EndReason { get; private set; }

		public LadderRun Run => _run;

		public float Progress => _track.Progress;

		public ClimbPoint HeldAt => _run.AsOne.ClimbPositionAt(_track.Progress, _limits.BodyOffsetMetres);

		private ClimbSession(LadderRun run, ClimbLimits limits, float startProgress, ClimbPoint mountedFrom, ClimbHeading facedFrom)
		{
			_run = run;
			_limits = limits;
			_track = new ClimbTrack(run.Height, limits, startProgress);
			_mountedFrom = mountedFrom;
			_facedFrom = (facedFrom.IsKnown ? facedFrom : run.AsOne.FacingWhileClimbing);
			Phase = ((limits.AlignSeconds > 0f) ? ClimbPhase.Aligning : ClimbPhase.OnTheLadder);
		}

		public bool NearTheTop(float withinMetres)
		{
			return _track.Progress >= _run.Height - Math.Max(0f, withinMetres);
		}

		public static bool TryStart(LadderRun run, in ClimberState climber, ClimbLimits limits, bool laddersEnabled, bool byDeliberateUse, out ClimbSession session, out MountRefusal refusal)
		{
			if (run == null)
			{
				throw new ArgumentNullException("run");
			}
			if (limits == null)
			{
				throw new ArgumentNullException("limits");
			}
			session = null;
			MountDecision mountDecision = LadderMount.Decide(run.AsOne, in climber, limits, laddersEnabled, byDeliberateUse);
			if (!mountDecision.Allowed)
			{
				refusal = mountDecision.Refusal;
				return false;
			}
			refusal = MountRefusal.Unspecified;
			session = new ClimbSession(run, limits, mountDecision.Progress, climber.Feet, climber.Looking);
			return true;
		}

		public ClimbStep Step(in ClimbFrame frame)
		{
			if (Phase == ClimbPhase.Ended)
			{
				return ClimbStep.AlreadyOver(EndReason);
			}
			ClimbEndReason climbEndReason = ClimbSafety.MustEnd(frame.Conditions, _limits);
			if (climbEndReason != ClimbEndReason.Unspecified)
			{
				return End(climbEndReason, ClimbSafety.Release(climbEndReason));
			}
			if (frame.LetGoRequested)
			{
				return End(ClimbEndReason.Unspecified, ClimbExit.LetGo(deliberate: true));
			}
			float num = frame.DeltaSeconds;
			if (float.IsNaN(num) || num < 0f)
			{
				num = 0f;
			}
			if (Phase == ClimbPhase.Aligning)
			{
				return Align(num);
			}
			ClimbMotion climbMotion = _track.Step(frame.ClimbInput, num);
			switch (climbMotion)
			{
			case ClimbMotion.PressingAgainstTheTop:
			{
				ClimbExitPlan? climbExitPlan = ClimbExit.OverTheTop(_run.AsOne, frame.TopLanding, _limits);
				if (climbExitPlan.HasValue)
				{
					return End(ClimbEndReason.Unspecified, climbExitPlan.Value);
				}
				break;
			}
			case ClimbMotion.StandingOnTheGround:
				return End(ClimbEndReason.Unspecified, ClimbExit.OffTheBottom(_run.AsOne, _limits));
			}
			return ClimbStep.Continue(HeldAt, _run.AsOne.FacingWhileClimbing, _track.StaminaFor(num), Telemetry(climbMotion, 1f));
		}

		public ClimbStep Fault()
		{
			return Stop(ClimbEndReason.ControllerFault);
		}

		public ClimbStep Stop(ClimbEndReason reason)
		{
			if (reason == ClimbEndReason.Unspecified)
			{
				throw new ArgumentOutOfRangeException("reason", "A climb does not end without a reason.");
			}
			if (Phase == ClimbPhase.Ended)
			{
				return ClimbStep.AlreadyOver(EndReason);
			}
			return End(reason, ClimbSafety.Release(reason));
		}

		private ClimbStep Align(float delta)
		{
			_aligned += delta;
			float num = ((_limits.AlignSeconds <= 0f) ? 1f : Math.Min(1f, Math.Max(0f, _aligned / _limits.AlignSeconds)));
			if (num >= 1f)
			{
				Phase = ClimbPhase.OnTheLadder;
			}
			float fraction = num * num * (3f - 2f * num);
			ClimbPoint bodyTarget = Between(_mountedFrom, HeldAt, fraction);
			ClimbHeading facing = Between(_facedFrom, _run.AsOne.FacingWhileClimbing, fraction);
			return ClimbStep.Continue(bodyTarget, facing, 0f, Telemetry(ClimbMotion.Resting, num));
		}

		private ClimbStep End(ClimbEndReason reason, ClimbExitPlan exit)
		{
			Phase = ClimbPhase.Ended;
			EndReason = reason;
			return ClimbStep.Ending(exit, reason);
		}

		private ClimbTelemetry Telemetry(ClimbMotion motion, float alignFraction)
		{
			return new ClimbTelemetry(Phase, motion, _track.Progress, _run.Height, _track.Velocity, _track.AnimationSpeed(), _run.AsOne.RungAt(_track.Progress), alignFraction, _run.AsOne.FacingWhileClimbing);
		}

		private static ClimbPoint Between(ClimbPoint from, ClimbPoint to, float fraction)
		{
			return new ClimbPoint(from.X + (to.X - from.X) * fraction, from.Y + (to.Y - from.Y) * fraction, from.Z + (to.Z - from.Z) * fraction);
		}

		private static ClimbHeading Between(ClimbHeading from, ClimbHeading to, float fraction)
		{
			if (!from.IsKnown)
			{
				return to;
			}
			if (!to.IsKnown)
			{
				return from;
			}
			ClimbHeading result = ClimbHeading.FromXz(from.X + (to.X - from.X) * fraction, from.Z + (to.Z - from.Z) * fraction);
			if (!result.IsKnown)
			{
				return to;
			}
			return result;
		}
	}
	internal enum ClimbMotion
	{
		Unspecified,
		Resting,
		Climbing,
		Descending,
		PressingAgainstTheTop,
		StandingOnTheGround
	}
	internal sealed class ClimbTrack
	{
		private readonly float _span;

		private readonly ClimbLimits _limits;

		public float Progress { get; private set; }

		public ClimbMotion Motion { get; private set; }

		public float Velocity { get; private set; }

		public bool AtTop => Progress >= _span - 0.001f;

		public bool AtBottom => Progress <= 0.001f;

		public ClimbTrack(float span, ClimbLimits limits, float startProgress)
		{
			if (limits == null)
			{
				throw new ArgumentNullException("limits");
			}
			if (float.IsNaN(span) || span <= 0f)
			{
				throw new ArgumentOutOfRangeException("span", "A run with no height is not climbable.");
			}
			_span = span;
			_limits = limits;
			Progress = Clamp(startProgress);
			Motion = ClimbMotion.Resting;
		}

		public ClimbMotion Step(float input, float deltaSeconds)
		{
			if (float.IsNaN(input) || float.IsNaN(deltaSeconds) || deltaSeconds <= 0f)
			{
				Velocity = 0f;
				Motion = ClimbMotion.Resting;
				return Motion;
			}
			float num = Math.Min(Math.Max(input, -1f), 1f);
			if (Math.Abs(num) < 0.1f)
			{
				Velocity = 0f;
				Motion = ClimbMotion.Resting;
				return Motion;
			}
			float progress = Progress;
			Velocity = num * _limits.EffectiveClimbSpeed;
			Progress = Clamp(Progress + Velocity * deltaSeconds);
			float num2 = Progress - progress;
			Velocity = ((deltaSeconds > 0f) ? (num2 / deltaSeconds) : 0f);
			if (num > 0f)
			{
				Motion = (AtTop ? ClimbMotion.PressingAgainstTheTop : ClimbMotion.Climbing);
			}
			else
			{
				Motion = (AtBottom ? ClimbMotion.StandingOnTheGround : ClimbMotion.Descending);
			}
			return Motion;
		}

		public void MoveTo(float progress)
		{
			Progress = Clamp(progress);
			Velocity = 0f;
			Motion = ClimbMotion.Resting;
		}

		public float AnimationSpeed()
		{
			float effectiveClimbSpeed = _limits.EffectiveClimbSpeed;
			if (!(effectiveClimbSpeed <= 0f))
			{
				return Velocity / effectiveClimbSpeed;
			}
			return 0f;
		}

		public float StaminaFor(float deltaSeconds)
		{
			if (_limits.StaminaPerSecond <= 0f || deltaSeconds <= 0f || Math.Abs(Velocity) < 0.01f)
			{
				return 0f;
			}
			return _limits.StaminaPerSecond * deltaSeconds;
		}

		private float Clamp(float progress)
		{
			if (float.IsNaN(progress))
			{
				return Progress;
			}
			return Math.Min(Math.Max(progress, 0f), _span);
		}
	}
	internal readonly struct LadderGeometry
	{
		public const float MinimumClimbableHeight = 0.9f;

		public const float ConventionalRungPitch = 0.35f;

		public ClimbPoint Bottom { get; }

		public ClimbPoint Top { get; }

		public ClimbHeading StandingSide { get; }

		public float Width { get; }

		public float RungPitch { get; }

		public float Height => Math.Max(0f, Top.Y - Bottom.Y);

		public ClimbHeading FacingWhileClimbing => StandingSide.Opposite();

		public bool IsClimbable
		{
			get
			{
				if (Height >= 0.9f && StandingSide.IsKnown)
				{
					return Width > 0f;
				}
				return false;
			}
		}

		private LadderGeometry(ClimbPoint bottom, ClimbPoint top, ClimbHeading standingSide, float width, float rungPitch)
		{
			Bottom = bottom;
			Top = top;
			StandingSide = standingSide;
			Width = width;
			RungPitch = rungPitch;
		}

		public static bool TryMeasure(ClimbPoint bottom, ClimbPoint top, ClimbHeading standingSide, float width, float rungPitch, out LadderGeometry geometry)
		{
			geometry = default(LadderGeometry);
			if (!bottom.IsFinite || !top.IsFinite || !standingSide.IsKnown)
			{
				return false;
			}
			if (float.IsNaN(width) || width <= 0f || width > 4f)
			{
				return false;
			}
			if (float.IsNaN(rungPitch) || rungPitch <= 0f || rungPitch > 1f)
			{
				rungPitch = 0.35f;
			}
			if (top.Y - bottom.Y < 0.9f)
			{
				return false;
			}
			geometry = new LadderGeometry(bottom, top, standingSide, width, rungPitch);
			return true;
		}

		public ClimbPoint PointAt(float progress)
		{
			float height = Height;
			float num = ((height <= 0f) ? 0f : Math.Min(Math.Max(progress, 0f), height));
			float num2 = ((height <= 0f) ? 0f : (num / height));
			return new ClimbPoint(Bottom.X + (Top.X - Bottom.X) * num2, Bottom.Y + (Top.Y - Bottom.Y) * num2, Bottom.Z + (Top.Z - Bottom.Z) * num2);
		}

		public float ProgressOf(ClimbPoint point)
		{
			return Math.Min(Math.Max(point.Y - Bottom.Y, 0f), Height);
		}

		public float SidewaysOffsetOf(ClimbPoint point)
		{
			ClimbPoint climbPoint = PointAt(ProgressOf(point));
			float num = point.X - climbPoint.X;
			float num2 = point.Z - climbPoint.Z;
			float num3 = num * StandingSide.X + num2 * StandingSide.Z;
			float num4 = num - StandingSide.X * num3;
			float num5 = num2 - StandingSide.Z * num3;
			return (float)Math.Sqrt(num4 * num4 + num5 * num5);
		}

		public float ReachFrom(ClimbPoint point)
		{
			ClimbPoint climbPoint = PointAt(ProgressOf(point));
			float num = point.X - climbPoint.X;
			float num2 = point.Z - climbPoint.Z;
			return num * StandingSide.X + num2 * StandingSide.Z;
		}

		public ClimbPoint ClimbPositionAt(float progress, float bodyOffset)
		{
			return PointAt(progress).Offset(StandingSide, bodyOffset);
		}

		public int RungAt(float progress)
		{
			float num = ((RungPitch <= 0f) ? 0.35f : RungPitch);
			return (int)Math.Floor(Math.Min(Math.Max(progress, 0f), Height) / num);
		}

		public override string ToString()
		{
			return "ladder " + Bottom.ToString() + " -> " + Top.ToString() + " (" + Height.ToString("0.##") + " m, standing side " + StandingSide.ToString() + ")";
		}
	}
	internal enum MountRefusal
	{
		Unspecified,
		NotClimbable,
		TooFar,
		OffToTheSide,
		WrongSide,
		OutOfSpan,
		LookingAway,
		CharacterBusy,
		Disabled,
		AlreadyClimbing
	}
	internal readonly struct ClimberState
	{
		public ClimbPoint Feet { get; }

		public ClimbHeading Looking { get; }

		public bool CanClimbNow { get; }

		public bool AlreadyClimbing { get; }

		public ClimberState(ClimbPoint feet, ClimbHeading looking, bool canClimbNow, bool alreadyClimbing)
		{
			Feet = feet;
			Looking = looking;
			CanClimbNow = canClimbNow;
			AlreadyClimbing = alreadyClimbing;
		}
	}
	internal readonly struct MountDecision
	{
		public bool Allowed { get; }

		public MountRefusal Refusal { get; }

		public float Progress { get; }

		public ClimbPoint Position { get; }

		public ClimbHeading Facing { get; }

		private MountDecision(bool allowed, MountRefusal refusal, float progress, ClimbPoint position, ClimbHeading facing)
		{
			Allowed = allowed;
			Refusal = refusal;
			Progress = progress;
			Position = position;
			Facing = facing;
		}

		public static MountDecision Allow(float progress, ClimbPoint position, ClimbHeading facing)
		{
			return new MountDecision(allowed: true, MountRefusal.Unspecified, progress, position, facing);
		}

		public static MountDecision Refuse(MountRefusal refusal)
		{
			if (refusal == MountRefusal.Unspecified)
			{
				throw new ArgumentOutOfRangeException("refusal", "A refusal needs a reason.");
			}
			return new MountDecision(allowed: false, refusal, 0f, default(ClimbPoint), ClimbHeading.None);
		}
	}
	internal static class LadderMount
	{
		public static MountDecision Decide(in LadderGeometry ladder, in ClimberState climber, ClimbLimits limits, bool laddersEnabled = true, bool byDeliberateUse = false)
		{
			if (limits == null)
			{
				throw new ArgumentNullException("limits");
			}
			if (!laddersEnabled)
			{
				return MountDecision.Refuse(MountRefusal.Disabled);
			}
			if (climber.AlreadyClimbing)
			{
				return MountDecision.Refuse(MountRefusal.AlreadyClimbing);
			}
			if (!climber.CanClimbNow)
			{
				return MountDecision.Refuse(MountRefusal.CharacterBusy);
			}
			if (!ladder.IsClimbable)
			{
				return MountDecision.Refuse(MountRefusal.NotClimbable);
			}
			if (!climber.Feet.IsFinite)
			{
				return MountDecision.Refuse(MountRefusal.TooFar);
			}
			float num = climber.Feet.Y - ladder.Top.Y;
			float num2 = ladder.Bottom.Y - climber.Feet.Y;
			if (num > limits.MountHeightMarginMetres || num2 > limits.MountHeightMarginMetres)
			{
				return MountDecision.Refuse(MountRefusal.OutOfSpan);
			}
			float num3 = ladder.ReachFrom(climber.Feet);
			if (num3 < -0.05f)
			{
				return MountDecision.Refuse(MountRefusal.WrongSide);
			}
			if (num3 > limits.MountReachMetres)
			{
				return MountDecision.Refuse(MountRefusal.TooFar);
			}
			if (ladder.SidewaysOffsetOf(climber.Feet) > ladder.Width * 0.5f + limits.MountSideMarginMetres)
			{
				return MountDecision.Refuse(MountRefusal.OffToTheSide);
			}
			if (!byDeliberateUse)
			{
				ClimbHeading facingWhileClimbing = ladder.FacingWhileClimbing;
				if (climber.Looking.Agreement(facingWhileClimbing) < limits.MountFacingAgreement)
				{
					return MountDecision.Refuse(MountRefusal.LookingAway);
				}
			}
			float progress = ladder.ProgressOf(climber.Feet);
			return MountDecision.Allow(progress, ladder.ClimbPositionAt(progress, limits.BodyOffsetMetres), ladder.FacingWhileClimbing);
		}

		public static string Explain(MountRefusal refusal)
		{
			return refusal switch
			{
				MountRefusal.NotClimbable => "That is too short to climb; just step up.", 
				MountRefusal.TooFar => "Stand closer to the ladder.", 
				MountRefusal.OffToTheSide => "Stand in front of the ladder.", 
				MountRefusal.WrongSide => "The rungs are on the other side.", 
				MountRefusal.OutOfSpan => "The ladder does not reach here.", 
				MountRefusal.LookingAway => "Face the ladder to climb it.", 
				MountRefusal.CharacterBusy => "Not while you are doing that.", 
				MountRefusal.Disabled => "Ladder climbing is switched off.", 
				MountRefusal.AlreadyClimbing => "You are already on a ladder.", 
				_ => "You cannot climb that.", 
			};
		}
	}
	internal sealed class LadderRun
	{
		private readonly List<LadderGeometry> _pieces;

		public IReadOnlyList<LadderGeometry> Pieces => _pieces;

		public ClimbPoint Bottom { get; }

		public ClimbPoint Top { get; }

		public float Height => Math.Max(0f, Top.Y - Bottom.Y);

		public LadderGeometry AsOne { get; private set; }

		internal LadderRun(List<LadderGeometry> pieces)
		{
			_pieces = pieces;
			Bottom = pieces[0].Bottom;
			Top = pieces[pieces.Count - 1].Top;
		}

		public LadderGeometry PieceAt(float progress)
		{
			float num = Bottom.Y + Math.Min(Math.Max(progress, 0f), Height);
			for (int i = 0; i < _pieces.Count; i++)
			{
				LadderGeometry result = _pieces[i];
				if (num <= result.Top.Y + 0.01f)
				{
					return result;
				}
			}
			return _pieces[_pieces.Count - 1];
		}

		public static bool TryJoin(IReadOnlyList<LadderGeometry> pieces, out LadderRun run)
		{
			run = null;
			if (pieces == null || pieces.Count == 0)
			{
				return false;
			}
			List<LadderGeometry> list = new List<LadderGeometry>(pieces);
			list.Sort((LadderGeometry left, LadderGeometry right) => left.Bottom.Y.CompareTo(right.Bottom.Y));
			for (int num = 0; num < list.Count; num++)
			{
				if (!list[num].IsClimbable)
				{
					return false;
				}
				if (num != 0)
				{
					LadderGeometry ladderGeometry = list[num - 1];
					LadderGeometry ladderGeometry2 = list[num];
					if (ladderGeometry.StandingSide.Agreement(ladderGeometry2.StandingSide) < 0.98f)
					{
						return false;
					}
					float num2 = ladderGeometry2.Bottom.Y - ladderGeometry.Top.Y;
					if (num2 < -0.35f || num2 > 0.35f)
					{
						return false;
					}
					if (ladderGeometry.Top.HorizontalDistanceTo(ladderGeometry2.Bottom) > 0.35f)
					{
						return false;
					}
				}
			}
			if (!LadderGeometry.TryMeasure(list[0].Bottom, list[list.Count - 1].Top, list[0].StandingSide, list[0].Width, list[0].RungPitch, out var geometry))
			{
				return false;
			}
			run = new LadderRun(list)
			{
				AsOne = geometry
			};
			return true;
		}

		public static LadderRun Single(in LadderGeometry piece)
		{
			if (!piece.IsClimbable)
			{
				throw new ArgumentOutOfRangeException("piece", "That piece is not climbable.");
			}
			return new LadderRun(new List<LadderGeometry> { piece })
			{
				AsOne = piece
			};
		}
	}
}
namespace TheConcernedCat.ConcernedLadders
{
	internal static class LaddersPaths
	{
		internal static string Root => Path.Combine(Paths.ConfigPath, "ConcernedCatMods", "ConcernedLadders");
	}
	[BepInPlugin("com.theconcernedcat.valheim.concernedladders", "Concerned Ladders", "1.0.0")]
	[BepInDependency("com.jotunn.jotunn", "2.30.2")]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.theconcernedcat.valheim.concernedladders";

		public const string PluginName = "Concerned Ladders";

		public const string PluginVersion = "1.0.0";

		private ConfigEntry<bool>? _enabled;

		private LadderRuntime? _runtime;

		private LadderPieceRegistration? _pieces;

		private LadderSkill? _skill;

		private OriginalClimbPresenter? _presenter;

		private LadderSnapAlignment? _snap;

		private void Awake()
		{
			_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ClimbingEnabled", true, "Enable climbing on Concerned Ladders. Disabling restores player control; placed ladder prefabs remain registered. Restart after changing the installed plugin set.");
			_snap = new LadderSnapAlignment(delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)message);
			});
			if (!_snap.Install("com.theconcernedcat.valheim.concernedladders.snap"))
			{
				_snap = null;
			}
			_pieces = new LadderPieceRegistration(delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)message);
			}, _snap != null);
			_pieces.Install();
			_skill = new LadderSkill(delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)message);
			});
			_skill.Register();
			_runtime = new LadderRuntime(delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)message);
			}, _skill);
			_presenter = new OriginalClimbPresenter(delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)message);
			});
			bool flag;
			try
			{
				flag = _presenter.Install("com.theconcernedcat.valheim.concernedladders.presentation");
			}
			catch (Exception exception)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Original climbing animation unavailable (" + SafeFailure.Brief(exception) + "); the walking pose is used."));
				flag = false;
			}
			if (flag)
			{
				_runtime.SetPresenter(_presenter);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Climbing gait source LadderGait.blend " + "50bc77aeab206126c363b88bd8b3801e5bd1b471a018c447a310344df2b20a16".Substring(0, 12) + " (Blender 5.1.2)."));
			}
			else
			{
				try
				{
					_presenter.Dispose();
				}
				catch
				{
				}
				_presenter = null;
			}
			_runtime.Enabled = _enabled.Value && _pieces.IsReady;
			_runtime.Install("com.theconcernedcat.valheim.concernedladders.motor");
			string text = typeof(Plugin).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? "1.0.0";
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Concerned Ladders " + text + " loaded."));
		}

		private void Update()
		{
			if (_runtime != null && _enabled != null)
			{
				_runtime.Enabled = _enabled.Value && (_pieces?.IsReady ?? false);
				_runtime.Update(Time.deltaTime);
			}
		}

		private void OnDestroy()
		{
			try
			{
				_runtime?.Dispose();
				_presenter?.Dispose();
			}
			catch (Exception exception)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Ladder cleanup failed: " + SafeFailure.Brief(exception)));
			}
			_snap?.Dispose();
			_pieces?.Dispose();
			_snap = null;
			_runtime = null;
			_pieces = null;
		}
	}
}
namespace TheConcernedCat.ConcernedLadders.Runtime
{
	internal interface IClimbPresenter : IDisposable
	{
		bool Install(string harmonyId);

		void Publish(Player player, in ClimbPresentationFrame frame);

		void End(Player? player, ClimbEnd reason);

		void Update(float deltaTime);
	}
	internal sealed class LadderClearance
	{
		private const float Skin = 0.02f;

		private readonly RaycastHit[] _hits = (RaycastHit[])(object)new RaycastHit[64];

		private readonly Collider[] _overlaps = (Collider[])(object)new Collider[64];

		private readonly Player _player;

		private readonly CapsuleCollider _capsule;

		private readonly int _mask;

		private readonly Vector3 _centreOffset;

		private readonly float _halfSegment;

		private readonly float _groundLift;

		private readonly HashSet<LadderSegment> _run = new HashSet<LadderSegment>();

		private const float GroundClearance = 0.05f;

		private static readonly Vector3[] Footprint = (Vector3[])(object)new Vector3[8]
		{
			Vector3.forward,
			Vector3.back,
			Vector3.left,
			Vector3.right,
			new Vector3(0.7071f, 0f, 0.7071f),
			new Vector3(-0.7071f, 0f, 0.7071f),
			new Vector3(0.7071f, 0f, -0.7071f),
			new Vector3(-0.7071f, 0f, -0.7071f)
		};

		private static readonly int TerrainLayer = LayerMask.NameToLayer("terrain");

		internal float Radius { get; }

		internal float Height { get; }

		internal BlockerKind LastBlocker { get; private set; }

		internal string LastBlockerName { get; private set; } = string.Empty;

		internal float BottomGap { get; }

		internal LadderClearance(Player player, CapsuleCollider capsule, Vector3 feet)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			_player = player;
			_capsule = capsule;
			Vector3 lossyScale = ((Component)capsule).transform.lossyScale;
			if (capsule.direction != 1 || !((Collider)capsule).enabled || !LadderSegment.Finite(lossyScale))
			{
				throw new InvalidOperationException("The player's capsule is not a supported upright capsule.");
			}
			Radius = capsule.radius * Mathf.Max(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.z));
			float num = (Height = Mathf.Max(2f * Radius, capsule.height * Mathf.Abs(lossyScale.y)));
			_halfSegment = Mathf.Max(0f, num * 0.5f - Radius);
			_centreOffset = ((Component)capsule).transform.TransformPoint(capsule.center) - ((Component)player).transform.position;
			if (!LadderRulesFinite(Radius) || !LadderRulesFinite(num) || !LadderSegment.Finite(_centreOffset) || Mathf.Abs(_centreOffset.x) > 0.05f || Mathf.Abs(_centreOffset.z) > 0.05f || Radius < 0.05f || Radius > 1f || num > 4f)
			{
				throw new InvalidOperationException("The player's capsule has unsupported dimensions.");
			}
			BottomGap = _centreOffset.y - num * 0.5f;
			_groundLift = Mathf.Max(0f, 0.05f - BottomGap);
			_mask = LayerMask.GetMask(new string[10] { "piece", "Default", "static_solid", "Default_small", "terrain", "blocker", "vehicle", "character", "character_net", "character_ghost" });
			if (_mask == 0)
			{
				throw new InvalidOperationException("Collision layers were unavailable.");
			}
		}

		internal void SetRun(IEnumerable<LadderSegment> segments)
		{
			_run.Clear();
			foreach (LadderSegment segment in segments)
			{
				if ((Object)(object)segment != (Object)null)
				{
					_run.Add(segment);
				}
			}
		}

		internal void ClearBlocker()
		{
			LastBlocker = BlockerKind.None;
			LastBlockerName = string.Empty;
		}

		internal bool IsUnchanged()
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: 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_00db: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_capsule == (Object)null || !((Collider)_capsule).enabled || _capsule.direction != 1)
			{
				return false;
			}
			Vector3 lossyScale = ((Component)_capsule).transform.lossyScale;
			float num = _capsule.radius * Mathf.Max(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.z));
			float num2 = Mathf.Max(2f * num, _capsule.height * Mathf.Abs(lossyScale.y));
			Vector3 val = ((Component)_capsule).transform.TransformPoint(_capsule.center) - ((Component)_player).transform.position;
			if (LadderSegment.Finite(lossyScale) && LadderRulesFinite(num) && LadderRulesFinite(num2) && LadderSegment.Finite(val) && Vector3.Distance(val, _centreOffset) < 0.001f && Mathf.Abs(num - Radius) < 0.001f)
			{
				return Mathf.Abs(num2 * 0.5f - num - _halfSegment) < 0.001f;
			}
			return false;
		}

		internal bool HasRoom(Vector3 feet)
		{
			//IL_0000: 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_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if (!LadderSegment.Finite(feet))
			{
				return false;
			}
			CapsuleAt(feet, out var low, out var high);
			int num = Physics.OverlapCapsuleNonAlloc(low, high, Mathf.Max(0.02f, Radius - 0.02f), _overlaps, _mask, (QueryTriggerInteraction)1);
			if (num >= _overlaps.Length)
			{
				Blocked(BlockerKind.Overflow, "query overflow");
				return false;
			}
			for (int i = 0; i < num; i++)
			{
				if (Solid(_overlaps[i]))
				{
					Classify(_overlaps[i]);
					return false;
				}
			}
			return true;
		}

		internal bool CanMove(Vector3 from, Vector3 to)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: 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_0040: 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_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_0061: 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)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			if (!LadderSegment.Finite(from) || !LadderSegment.Finite(to) || !HasRoom(to))
			{
				return false;
			}
			Vector3 val = to - from;
			float magnitude = ((Vector3)(ref val)).magnitude;
			if (magnitude < 0.0001f)
			{
				return true;
			}
			CapsuleAt(from, out var low, out var high);
			Vector3 val2 = val / magnitude;
			int num = Physics.CapsuleCastNonAlloc(low, high, Mathf.Max(0.02f, Radius - 0.02f), val2, _hits, magnitude, _mask, (QueryTriggerInteraction)1);
			if (num >= _hits.Length)
			{
				Blocked(BlockerKind.Overflow, "query overflow");
				return false;
			}
			for (int i = 0; i < num; i++)
			{
				RaycastHit val3 = _hits[i];
				if (Solid(((RaycastHit)(ref val3)).collider) && (!(((RaycastHit)(ref val3)).distance <= 0.02f) || !(Vector3.Dot(((RaycastHit)(ref val3)).normal, val2) >= -0.01f)))
				{
					Classify(((RaycastHit)(ref val3)).collider);
					return false;
				}
			}
			return true;
		}

		internal bool CanFollow(Vector3 from, Vector3[] points)
		{
			//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_000f: 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_001a: 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)
			foreach (Vector3 val in points)
			{
				if (!CanMove(from, val))
				{
					return false;
				}
				from = val;
			}
			return true;
		}

		internal bool HasLineOfSight(Vector3 from, Vector3 to)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = to - from;
			float magnitude = ((Vector3)(ref val)).magnitude;
			if (!LadderSegment.Finite(from) || !LadderSegment.Finite(to))
			{
				Blocked(BlockerKind.Overflow, "not finite");
				return false;
			}
			if (magnitude < 0.001f)
			{
				return true;
			}
			int num = Physics.RaycastNonAlloc(from, val / magnitude, _hits, magnitude, _mask, (QueryTriggerInteraction)1);
			if (num >= _hits.Length)
			{
				Blocked(BlockerKind.Overflow, "query overflow");
				return false;
			}
			for (int i = 0; i < num; i++)
			{
				Collider collider = ((RaycastHit)(ref _hits[i])).collider;
				if (Solid(collider) && !OwnRun(collider))
				{
					Classify(collider);
					return false;
				}
			}
			return true;
		}

		internal bool Supported(Vector3 feet)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			Vector3 floor;
			return TrySurface(feet, 0.15f, 0.2f, out floor);
		}

		internal bool SupportedFootprint(Vector3 feet)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			string why;
			return SupportedFootprint(feet, out why);
		}

		internal bool SupportedFootprint(Vector3 feet, out string why)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return SupportedFootprint(feet, 0.15f, out why);
		}

		internal bool SupportedFootprint(Vector3 feet, float step, out string why)
		{
			//IL_0008: 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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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)
			why = "";
			if (!TrySurface(feet, 0.15f, 0.2f, out var floor))
			{
				why = "no surface under the centre";
				return false;
			}
			float num = Radius * 0.9f;
			Vector3[] footprint = Footprint;
			foreach (Vector3 val in footprint)
			{
				if (!TrySurface(feet + val * num, 0.15f, 0.2f, out var floor2))
				{
					why = "no surface at an edge";
					return false;
				}
				if (Mathf.Abs(floor2.y - floor.y) > step)
				{
					why = "a step at an edge";
					return false;
				}
			}
			return true;
		}

		internal bool TryTopSurface(Vector3 desired, out Vector3 floor)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return TrySurface(desired, 0.75f, 0.5f, out floor);
		}

		internal bool TryNearbySurface(Vector3 desired, out Vector3 floor)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return TrySurface(desired, 0.45f, 0.45f, out floor);
		}

		internal bool TryLandingSurface(Vector3 desired, float above, float below, out Vector3 floor)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return TrySurface(desired, above, below, out floor);
		}

		internal bool TryFloorBelow(Vector3 feet, float maxDrop, out float floorY)
		{
			//IL_0008: 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)
			floorY = float.NaN;
			if (!TrySurface(feet, 0.15f, maxDrop, out var floor))
			{
				return false;
			}
			floorY = floor.y;
			return true;
		}

		private bool TrySurface(Vector3 desired, float above, float below, out Vector3 floor)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: 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_00c1: 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_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			floor = default(Vector3);
			if (!LadderSegment.Finite(desired))
			{
				return false;
			}
			int num = Physics.RaycastNonAlloc(desired + Vector3.up * above, Vector3.down, _hits, above + below, _mask, (QueryTriggerInteraction)1);
			if (num >= _hits.Length)
			{
				return false;
			}
			float num2 = float.MaxValue;
			bool result = false;
			for (int i = 0; i < num; i++)
			{
				RaycastHit val = _hits[i];
				if (Solid(((RaycastHit)(ref val)).collider) && !(((RaycastHit)(ref val)).normal.y < 0.65f) && !((Object)(object)((Component)((RaycastHit)(ref val)).collider).GetComponentInParent<LadderSegment>() != (Object)null) && !((Object)(object)((Component)((RaycastHit)(ref val)).collider).GetComponentInParent<Character>() != (Object)null))
				{
					float num3 = Mathf.Abs(((RaycastHit)(ref val)).point.y - desired.y);
					if (!(num3 >= num2))
					{
						num2 = num3;
						floor = new Vector3(desired.x, ((RaycastHit)(ref val)).point.y + 0.02f, desired.z);
						result = true;
					}
				}
			}
			return result;
		}

		private bool OwnRun(Collider collider)
		{
			LadderSegment componentInParent = ((Component)collider).GetComponentInParent<LadderSegment>();
			if ((Object)(object)componentInParent != (Object)null)
			{
				return _run.Contains(componentInParent);
			}
			return false;
		}

		private void Classify(Collider collider)
		{
			if (OwnRun(collider))
			{
				Blocked(BlockerKind.OwnLadder, ((Object)collider).name);
				return;
			}
			Character componentInParent = ((Component)collider).GetComponentInParent<Character>();
			if ((Object)(object)componentInParent != (Object)null)
			{
				Blocked(BlockerKind.Actor, componentInParent.m_name);
				return;
			}
			Piece componentInParent2 = ((Component)collider).GetComponentInParent<Piece>();
			if ((Object)(object)componentInParent2 != (Object)null)
			{
				Blocked(BlockerKind.Building, componentInParent2.m_name);
			}
			else if ((Object)(object)((Component)collider).GetComponent<Heightmap>() != (Object)null || ((Component)collider).gameObject.layer == TerrainLayer)
			{
				Blocked(BlockerKind.Terrain, "terrain");
			}
			else
			{
				Blocked(BlockerKind.Other, ((Object)collider).name);
			}
		}

		private void Blocked(BlockerKind kind, string name)
		{
			LastBlocker = kind;
			string text = name ?? string.Empty;
			LastBlockerName = ((text.Length > 48) ? text.Substring(0, 48) : text);
		}

		private bool Solid(Collider collider)
		{
			if ((Object)(object)collider != (Object)null && !collider.isTrigger)
			{
				return !((Component)collider).transform.IsChildOf(((Component)_player).transform);
			}
			return false;
		}

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

		private void CapsuleAt(Vector3 feet, out Vector3 low, out Vector3 high)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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_0039: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = feet + _centreOffset + Vector3.up * _groundLift;
			low = val - Vector3.up * _halfSegment;
			high = val + Vector3.up * _halfSegment;
		}
	}
	internal sealed class LadderMotor : IDisposable
	{
		private const string ForemanMotor = "com.theconcernedcat.valheim.concernedforeman.ladders";

		private FieldRef<Character, Vector3>? _move;

		private FieldRef<Character, float>? _fallHeight;

		private FieldRef<Character, float>? _groundTouch;

		private FieldRef<Character, Vector3>? _walkVelocity;

		private FieldRef<Character, bool>? _run;

		private Func<Player, bool>? _takeInput;

		private FieldRef<Player, int>? _interactMask;

		private Harmony? _harmony;

		private MethodInfo? _motion;

		internal bool Ready { get; private set; }

		internal bool Install(string id, Action<string> log)
		{
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ef: Expected O, but got Unknown
			//IL_030b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0319: Expected O, but got Unknown
			//IL_0331: Unknown result type (might be due to invalid IL or missing references)
			//IL_033f: Expected O, but got Unknown
			//IL_0357: Unknown result type (might be due to invalid IL or missing references)
			//IL_0365: Expected O, but got Unknown
			//IL_037d: Unknown result type (might be due to invalid IL or missing references)
			//IL_038b: Expected O, but got Unknown
			try
			{
				_move = AccessTools.FieldRefAccess<Character, Vector3>("m_moveDir");
				_fallHeight = AccessTools.FieldRefAccess<Character, float>("m_maxAirAltitude");
				_groundTouch = AccessTools.FieldRefAccess<Character, float>("m_lastGroundTouch");
				_walkVelocity = AccessTools.FieldRefAccess<Character, Vector3>("m_currentVel");
				_run = AccessTools.FieldRefAccess<Character, bool>("m_run");
				_motion = AccessTools.Method(typeof(Character), "UpdateMotion", new Type[1] { typeof(float) }, (Type[])null);
				MethodInfo methodInfo = AccessTools.Method(typeof(Character), "Jump", new Type[1] { typeof(bool) }, (Type[])null);
				MethodInfo methodInfo2 = AccessTools.Method(typeof(Player), "TakeInput", Type.EmptyTypes, (Type[])null);
				MethodInfo methodInfo3 = AccessTools.Method(typeof(Player), "SetControls", new Type[12]
				{
					typeof(Vector3),
					typeof(bool),
					typeof(bool),
					typeof(bool),
					typeof(bool),
					typeof(bool),
					typeof(bool),
					typeof(bool),
					typeof(bool),
					typeof(bool),
					typeof(bool),
					typeof(bool)
				}, (Type[])null);
				MethodInfo methodInfo4 = AccessTools.Method(typeof(Player), "Interact", new Type[3]
				{
					typeof(GameObject),
					typeof(bool),
					typeof(bool)
				}, (Type[])null);
				if (_motion == null || methodInfo == null || methodInfo2 == null || methodInfo2.ReturnType != typeof(bool) || methodInfo3 == null || methodInfo4 == null || methodInfo4.ReturnType != typeof(void) || !HasParameters(methodInfo3, "jump", "crouch") || Array.FindIndex(methodInfo3.GetParameters(), (ParameterInfo parameter) => parameter.Name == "movedir" && parameter.ParameterType == typeof(Vector3)) < 0)
				{
					log("Ladder climbing unavailable: the installed game's motor/input API did not match. Pieces remain registered.");
					return false;
				}
				_takeInput = AccessTools.MethodDelegate<Func<Player, bool>>(methodInfo2, (object)null, true);
				try
				{
					if (AccessTools.Field(typeof(Player), "m_interactMask")?.FieldType == typeof(int))
					{
						_interactMask = AccessTools.FieldRefAccess<Player, int>("m_interactMask");
					}
				}
				catch
				{
					_interactMask = null;
				}
				_harmony = new Harmony(id);
				_harmony.Patch((MethodBase)_motion, new HarmonyMethod(typeof(LadderMotor), "BeforeMotion", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(LadderMotor), "BeforeJump", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(LadderMotor), "BeforeControls", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_harmony.Patch((MethodBase)methodInfo4, new HarmonyMethod(typeof(LadderMotor), "BeforeInteract", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Ready = true;
				return true;
			}
			catch (Exception exception)
			{
				log("Ladder climbing unavailable: motor binding failed (" + SafeFailure.Brief(exception) + ").");
				Dispose();
				return false;
			}
		}

		internal bool HasForemanMotor()
		{
			if (_motion == null)
			{
				return false;
			}
			Patches patchInfo = Harmony.GetPatchInfo((MethodBase)_motion);
			if (patchInfo == null)
			{
				return false;
			}
			foreach (string owner in patchInfo.Owners)
			{
				if (string.Equals(owner, "com.theconcernedcat.valheim.concernedforeman.ladders", StringComparison.Ordinal))
				{
					return true;
				}
			}
			return false;
		}

		internal bool TakesInput(Player player)
		{
			if (Ready && _takeInput != null)
			{
				return _takeInput(player);
			}
			return false;
		}

		internal Vector3 MoveDirection(Player player)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			if (_move == null)
			{
				return Vector3.zero;
			}
			return _move.Invoke((Character)(object)player);
		}

		internal float FallHeight(Player player)
		{
			if (_fallHeight == null)
			{
				return float.NaN;
			}
			return _fallHeight.Invoke((Character)(object)player);
		}

		internal bool RunIntent(Player player)
		{
			if (_run != null)
			{
				return _run.Invoke((Character)(object)player);
			}
			return false;
		}

		internal int InteractMask(Player player)
		{
			if (_interactMask == null)
			{
				return -1;
			}
			return _interactMask.Invoke(player);
		}

		internal void SetFallReference(Player player, float height)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if (_fallHeight != null && LadderSegment.Finite(new Vector3(0f, height, 0f)))
			{
				_fallHeight.Invoke((Character)(object)player) = height;
			}
		}

		internal void BeginAirborne(Player player, Vector3 horizontal)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if (_groundTouch != null)
			{
				_groundTouch.Invoke((Character)(object)player) = 1f;
			}
			SetWalkVelocity(player, horizontal);
		}

		internal void SetWalkVelocity(Player player, Vector3 horizontal)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			horizontal.y = 0f;
			if (_walkVelocity != null && LadderSegment.Finite(horizontal))
			{
				_walkVelocity.Invoke((Character)(object)player) = horizontal;
			}
		}

		public void Dispose()
		{
			Ready = false;
			try
			{
				Harmony? harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch
			{
			}
			_harmony = null;
		}

		private static bool BeforeMotion(Character __instance, float __0)
		{
			return !(LadderRuntime.Current?.Drive(__instance, __0) ?? false);
		}

		private static bool BeforeJump(Character __instance)
		{
			return !(LadderRuntime.Current?.Jump(__instance) ?? false);
		}

		private static void BeforeControls(Player __instance, Vector3 movedir, ref bool jump, ref bool crouch)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			LadderRuntime.Current?.Controls(__instance, movedir, ref jump, ref crouch);
		}

		private static bool BeforeInteract(Player __instance)
		{
			if (__instance == Player.m_localPlayer)
			{
				return !(LadderRuntime.Current?.SuppressInteract() ?? false);
			}
			return true;
		}

		private static bool HasParameters(MethodInfo method, params string[] names)
		{
			ParameterInfo[] parameters = method.GetParameters();
			foreach (string name in names)
			{
				if (Array.FindIndex(parameters, (ParameterInfo parameter) => parameter.Name == name && parameter.ParameterType == typeof(bool)) < 0)
				{
					return false;
				}
			}
			return true;
		}
	}
	internal sealed class LadderRuntime : IDisposable
	{
		private enum Mode
		{
			Off,
			Mounting,
			Climbing,
			Exiting
		}

		private struct Plan
		{
			internal LadderFace Face;

			internal float Lane;

			internal float Height;

			internal Vector3[] Route;

			internal bool Airborne;

			internal bool TopEntry;

			internal float Reach;
		}

		private enum Departure
		{
			Controlled,
			External,
			Jump,
			Push,
			Drop
		}

		private const float TopPlanBand = 1.3f;

		private const float TopEntryDrop = 1.3f;

		private const float TopEntrySightAbove = 0.2f;

		private const float IntermediateAbove = 1.15f;

		private const float IntermediateBelow = 0.45f;

		private const float TakeoverDistance = 0.5f;

		private const float TopBlockedBand = 2.2f;

		private static readonly CultureInfo Invariant = CultureInfo.InvariantCulture;

		private readonly Action<string> _log;

		private readonly LadderMotor _motor = new LadderMotor();

		private readonly LadderWalkPose _pose;

		private readonly LadderSkill _skill;

		private readonly ClimbLedger _ledger = new ClimbLedger();

		private readonly List<LadderSegment> _segments = new List<LadderSegment>();

		private readonly List<Vector3> _positions = new List<Vector3>();

		private readonly List<Quaternion> _rotations = new List<Quaternion>();

		private readonly List<float> _spanBottoms = new List<float>();

		private readonly List<float> _spanTops = new List<float>();

		private readonly List<LadderSegment> _hoverParts = new List<LadderSegment>();

		private LadderSegment? _hoverSegment;

		private LadderGeometry _hoverRun;

		private bool _hoverValid;

		private float _hoverTime;

		private int _hoverRevision = -1;

		private float _plannedScale = 1f;

		private Player? _player;

		private Player? _sessionOwner;

		private Rigidbody? _body;

		private ZNetView? _view;

		private LadderClearance? _clearance;

		private LadderGeometry _run;

		private Mode _mode;

		private LadderFace _face;

		private ClimbGait _gait;

		private Vector3[] _route = Array.Empty<Vector3>();

		private int _routeIndex;

		private float _laneDepth;

		private float _plane;

		private float _pitch;

		private float _gripHeight;

		private float _hangLowest;

		private float _transferStartHeight;

		private bool _gravityBefore;

		private bool _wrotePhysics;

		private float _cooldown;

		private float _poseSpeed;

		private float _topPlanTimer;

		private float _lastControlledHeight;

		private Vector3 _lastControlledPosition;

		private float _noticeCooldown;

		private float _compatibilityTimer;

		private int _mountFrame = -1;

		private bool _foremanConflict;

		private string _lastNotice = string.Empty;

		private bool _enabled = true;

		private readonly DropTrigger _drop = new DropTrigger();

		private int _departFrame = -1;

		private int _runRevision = -1;

		private Vector3 _commanded;

		private IClimbPresenter? _presenter;

		private int _session;

		private int _discontinuity;

		private int _geometryRevision;

		private RungProfile[]? _profiles;

		private int _profilesRevision = -1;

		private int _profilesSession = -1;

		private long _sample;

		private float _travel;

		private float _lastSampleY;

		private Mode _lastSampleMode;

		private bool _mountAirborne;

		private bool _exitIsTop;

		private bool _exitIsBottom;

		private bool _mountFromTop;

		private Vector3 _routeStart;

		private float _routeRise;

		private float _routeLength;

		private ClimbEnd _endHint;

		private readonly RaycastHit[] _buildHover = (RaycastHit[])(object)new RaycastHit[64];

		private const float TopExitSeconds = 0.45f;

		private const float SideExitSeconds = 0.45f;

		private const float BottomStepSeconds = 0.45f;

		private const float TopEntrySeconds = 0.45f;

		private const float QuickSeconds = 0.45f;

		private const float QuickRouteSpeed = 6f;

		private const float NoLandingStop = 1.3f;

		internal static LadderRuntime? Current { get; private set; }

		internal bool IsClimbing => _mode != Mode.Off;

		internal bool Ready => _motor.Ready;

		internal bool Enabled
		{
			get
			{
				return _enabled;
			}
			set
			{
				_enabled = value;
				if (!value && IsClimbing)
				{
					Release(Departure.Controlled);
				}
			}
		}

		private static bool LogLandings => ClimbTuning.LogLandingQueries;

		internal LadderRuntime(Action<string> log, LadderSkill skill)
		{
			_log = log ?? throw new ArgumentNullException("log");
			_skill = skill ?? throw new ArgumentNullException("skill");
			_pose = new LadderWalkPose(log);
		}

		internal void SetPresenter(IClimbPresenter? presenter)
		{
			_presenter = presenter;
		}

		internal bool Install(string harmonyId)
		{
			if (Current != null && Current != this)
			{
				_log("Ladder climbing unavailable: another Concerned Ladders runtime is already active.");
				return false;
			}
			Current = this;
			return _motor.Install(harmonyId, _log);
		}

		internal void Update(float deltaTime)
		{
			float num = LadderRules.FrameSeconds(deltaTime);
			_cooldown = Mathf.Max(0f, _cooldown - num);
			_noticeCooldown = Mathf.Max(0f, _noticeCooldown - num);
			_topPlanTimer = Mathf.Max(0f, _topPlanTimer - num);
			if (Player.m_localPlayer != _sessionOwner)
			{
				_ledger.Reset();
				_skill.Forget();
				_sessionOwner = Player.m_localPlayer;
			}
			try
			{
				_presenter?.Update(num);
			}
			catch (Exception exception)
			{
				PresenterFault(exception);
			}
			if (!IsClimbing)
			{
				TryBuildModeMount();
				return;
			}
			try
			{
				if ((Object)(object)_player == (Object)null || (Object)(object)Player.m_localPlayer != (Object)(object)_player || (Object)(object)ZNetScene.instance == (Object)null || !Enabled || (Object)(object)_view == (Object)null || !_view.IsValid() || !_view.IsOwner() || !Free(_player, attached: true))
				{
					Release(Departure.External);
					return;
				}
				_compatibilityTimer -= num;
				if (_compatibilityTimer <= 0f)
				{
					_compatibilityTimer = 1f;
					_foremanConflict = _motor.HasForemanMotor();
					if (_foremanConflict)
					{
						Notice("$cl_msg_foreman");
						Release(Departure.Controlled);
						return;
					}
				}
				bool flag = _motor.TakesInput(_player);
				bool gameplayFocus = GameplayFocus(_player);
				_drop.Observe(CrouchHeld(), gameplayFocus);
				_drop.Press(CrouchPressed(), gameplayFocus);
				ClimbAction climbAction = ClimbControls.Resolve(gameplayFocus, JumpPressed(), CrouchPressed(), UsePressed(), ((Character)_player).InPlaceMode());
				if (_mode == Mode.Climbing && climbAction == ClimbAction.Use && Time.frameCount != _mountFrame && !HoversInteractable(_player) && !TrySafeDismount())
				{
					Notice("$cl_msg_nolanding");
				}
				_pose.Apply((_mode == Mode.Exiting) ? _poseSpeed : ((_presenter != null || !flag) ? 0f : _poseSpeed));
			}
			catch (Exception exception2)
			{
				Fault(exception2);
			}
		}

		private static bool UsePressed()
		{
			if (ZInput.GetButtonDown("Use") || ZInput.GetButtonDown("JoyUse"))
			{
				return !Hud.InRadial();
			}
			return false;
		}

		private static bool JumpPressed()
		{
			if (!ZInput.GetButtonDown("Jump"))
			{
				return ZInput.GetButtonDown("JoyJump");
			}
			return true;
		}

		private void TryBuildModeMount()
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null || !((Character)localPlayer).InPlaceMode() || _cooldown > 0f || !_motor.Ready || !UsePressed() || JumpPressed() || !GameplayFocus(localPlayer) || (Object)(object)GameCamera.instance == (Object)null)
				{
					return;
				}
				int num = _motor.InteractMask(localPlayer);
				if (num == -1)
				{
					return;
				}
				Transform transform = ((Component)GameCamera.instance).transform;
				int num2 = Physics.RaycastNonAlloc(transform.position, transform.forward, _buildHover, 50f, num);
				if (num2 >= _buildHover.Length)
				{
					return;
				}
				float num3 = float.MaxValue;
				RaycastHit? val = null;
				for (int i = 0; i < num2 && i < _buildHover.Length; i++)
				{
					RaycastHit value = _buildHover[i];
					if (!((Object)(object)((RaycastHit)(ref value)).collider == (Object)null))
					{
						Rigidbody attachedRigidbody = ((RaycastHit)(ref value)).collider.attachedRigidbody;
						if ((!((Object)(object)attachedRigidbody != (Object)null) || !((Object)(object)((Component)attachedRigidbody).gameObject == (Object)(object)((Component)localPlayer).gameObject)) && ((RaycastHit)(ref value)).distance < num3)
						{
							num3 = ((RaycastHit)(ref value)).distance;
							val = value;
						}
					}
				}
				if (!val.HasValue)
				{
					return;
				}
				RaycastHit value2 = val.Value;
				LadderSegment componentInParent = ((Component)((RaycastHit)(ref value2)).collider).GetComponentInParent<LadderSegment>();
				if (!((Object)(object)componentInParent == (Object)null))
				{
					Vector3 eyePoint = ((Character)localPlayer).GetEyePoint();
					value2 = val.Value;
					if (!(Vector3.Distance(eyePoint, ((RaycastHit)(ref value2)).point) >= localPlayer.m_maxInteractDistance))
					{
						Use(componentInParent);
					}
				}
			}
			catch (Exception exception)
			{
				Fault(exception);
			}
		}

		private static bool CrouchPressed()
		{
			if (!ZInput.GetButtonDown("Crouch"))
			{
				return ZInput.GetButtonDown("JoyCrouch");
			}
			return true;
		}

		private static bool CrouchHeld()
		{
			if (!ZInput.GetButton("Crouch"))
			{
				return ZInput.GetButton("JoyCrouch");
			}
			return true;
		}

		internal void Controls(Player player, Vector3 movedir, ref bool jump, ref bool crouch)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			if (!IsClimbing || (Object)(object)_player == (Object)null || player != _player)
			{
				return;
			}
			try
			{
				bool num = GameplayFocus(player);
				if (!num)
				{
					_drop.Observe(CrouchHeld(), gameplayFocus: false);
				}
				ClimbAction climbAction = ClimbControls.Resolve(drop: num && _drop.Take(crouch), gameplayFocus: num, jump: jump, use: false, buildModeOwnsUse: false);
				jump = false;
				crouch = false;
				switch (climbAction)
				{
				case ClimbAction.Jump:
					JumpOff(player, CameraRelative(player, movedir));
					break;
				case ClimbAction.Drop:
					DropOff(player);
					break;
				}
			}
			catch (Exception exception)
			{
				Fault(exception);
			}
		}

		private static Vector3 CameraRelative(Player player, Vector3 movedir)
		{
			//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_0013: 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_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: 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)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			Vector3 lookDir = ((Character)player).GetLookDir();
			lookDir.y = 0f;
			if (!LadderSegment.Finite(movedir) || !LadderSegment.Finite(lookDir) || ((Vector3)(ref lookDir)).sqrMagnitude < 1E-06f)
			{
				return Vector3.zero;
			}
			((Vector3)(ref lookDir)).Normalize();
			return movedir.z * lookDir + movedir.x * Vector3.Cross(Vector3.up, lookDir);
		}

		private bool GameplayFocus(Player player)
		{
			if (_motor.TakesInput(player) && !InventoryGui.IsVisible() && !Hud.InRadial())
			{
				return !Hud.IsPieceSelectionVisible();
			}
			return false;
		}

		internal bool SuppressInteract()
		{
			if (Time.frameCount == _departFrame)
			{
				return true;
			}
			if (!IsClimbing || (Object)(object)_player == (Object)null || !GameplayFocus(_player))
			{
				return false;
			}
			if (!JumpPressed())
			{
				if (CrouchPressed())
				{
					if (!_drop.Armed)
					{
						return _drop.Pending;
					}
					return true;
				}
				return false;
			}
			return true;
		}

		private static bool HoversInteractable(Player player)
		{
			GameObject hoverObject = ((Humanoid)player).GetHoverObject();
			if ((Object)(object)hoverObject != (Object)null)
			{
				return hoverObject.GetComponentInParent<Interactable>() != null;
			}
			return false;
		}

		internal bool InReach(LadderSegment segment)
		{
			//IL_00b0: 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)
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null || (Object)(object)segment == (Object)null)
				{
					return false;
				}
				if (segment != _hoverSegment || _hoverRevision != LadderSegment.Revision || Time.time - _hoverTime > 0.5f)
				{
					_hoverSegment = segment;
					_hoverRevision = LadderSegment.Revision;
					_hoverTime = Time.time;
					_hoverValid = LadderSegment.TryRun(segment, _hoverParts, out _hoverRun);
					_hoverParts.Clear();
				}
				CapsuleCollider collider = ((Character)localPlayer).GetCollider();
				if (!_hoverValid || (Object)(object)collider == (Object)null)
				{
					return false;
				}
				float capsuleHeight = collider.height * Mathf.Abs(((Component)collider).transform.lossyScale.y);
				return ClimbPolicy.InReach(Reach(in _hoverRun, segment, ((Component)localPlayer).transform.position, capsuleHeight, ((Character)localPlayer).IsOnGround() && !StandsOnLadder(localPlayer)));
			}
			catch
			{
				return false;
			}
		}

		private static float Reach(in LadderGeometry geometry, LadderSegment segment, Vector3 feet, float capsuleHeight, bool supported)
		{
			//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_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			Vector3 point = feet + Vector3.up * (capsuleHeight * 0.72f);
			float planeDepth = (segment.BackFaceDepth + segment.FrontFaceDepth) * 0.5f;
			return ClimbPolicy.MountReach(in geometry, LadderSegment.Point(feet), LadderSegment.Point(point), planeDepth, segment.RungPitch * 0.5f, supported);
		}

		internal bool Use(LadderSegment segment)
		{
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
				{
					return false;
				}
				if (!_motor.Ready)
				{
					Notice("$cl_msg_startup");
					return true;
				}
				if (!Enabled)
				{
					Notice("$cl_msg_disabled");
					return true;
				}
				if (!_motor.TakesInput(localPlayer))
				{
					return false;
				}
				if (IsClimbing)
				{
					if (Time.frameCount != _mountFrame && !JumpPressed() && !TrySafeDismount())
					{
						Notice("$cl_msg_nolanding");
					}
					return true;
				}
				if (JumpPressed())
				{
					return true;
				}
				_foremanConflict = _motor.HasForemanMotor();
				if (_foremanConflict)
				{
					Notice("$cl_msg_foreman");
					return true;
				}
				Rigidbody component = ((Component)localPlayer).GetComponent<Rigidbody>();
				ZNetView component2 = ((Component)localPlayer).GetComponent<ZNetView>();
				CapsuleCollider collider = ((Character)localPlayer).GetCollider();
				if ((Object)(object)component == (Object)null || component.isKinematic || (Object)(object)component2 == (Object)null || !component2.IsValid() || (Object)(object)collider == (Object)null || !ClimbPolicy.MayAdmit(component2.IsOwner(), Free(localPlayer), overlayOpen: false, _cooldown))
				{
					Notice("$cl_msg_unavailable");
					return true;
				}
				if (!LadderSegment.TryRun(segment, _segments, out var run))
				{
					Notice("$cl_msg_invalidrun");
					return true;
				}
				LadderClearance ladderClearance = new LadderClearance(localPlayer, collider, component.position);
				ladderClearance.SetRun(_segments);
				if (!TryPlanMount(localPlayer, component, ladderClearance, in run, segment, out Plan plan, out ClimbRefusal refusal, out string detail))
				{
					Refuse(refusal, ladderClearance);
					_log("Ladder mount refused: " + refusal.ToString() + " (" + detail + ").");
					_segments.Clear();
					return true;
				}
				Commit(localPlayer, component, component2, ladderClearance, in run, segment, plan);
				return true;
			}
			catch (Exception exception)
			{
				Fault(exception);
				return true;
			}
		}

		private bool TryPlanMount(Player player, Rigidbody body, LadderClearance clearance, in LadderGeometry run, LadderSegment segment, out Plan plan, out ClimbRefusal refusal, out string detail)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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_00e6: 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_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: 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_01c7: 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_01d1: 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_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0303: Unknown result type (might be due to invalid IL or missing references)
			//IL_030a: Unknown result type (might be due to invalid IL or missing references)
			//IL_030f: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0338: Unknown result type (might be due to invalid IL or missing references)
			//IL_0326: 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_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0359: Unknown result type (might be due to invalid IL or missing references)
			//IL_03be: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0447: Unknown result type (might be due to invalid IL or missing references)
			//IL_0448: Unknown result type (might be due to invalid IL or missing references)
			//IL_044a: Unknown