Decompiled source of TrulySmoothLadders v0.5.4

TrulySmoothLadders.dll

Decompiled a month 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 BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JG224.ModCore.API;
using JG224.TrulySmoothLadders.Core;
using JG224.TrulySmoothLadders.Patches;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("TrulySmoothLadders")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.4.0")]
[assembly: AssemblyInformationalVersion("0.5.4")]
[assembly: AssemblyProduct("TrulySmoothLadders")]
[assembly: AssemblyTitle("TrulySmoothLadders")]
[assembly: AssemblyVersion("0.5.4.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace JG224.TrulySmoothLadders
{
	[BepInPlugin("jg224.TrulySmoothLadders", "TrulySmoothLadders", "0.5.4")]
	[BepInProcess("valheim.exe")]
	[BepInDependency("com.jg224.modcore", "0.5.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "jg224.TrulySmoothLadders";

		public const string PluginName = "TrulySmoothLadders";

		public const string PluginVersion = "0.5.4";

		public const string ModCoreGuid = "com.jg224.modcore";

		public const string GeneralTweaksGuid = "JG224.GeneralTweaks";

		public const int ProtocolVersion = 1;

		public static readonly ModuleId ModuleId = new ModuleId("trulysmoothladders");

		private readonly List<IDisposable> _registrations = new List<IDisposable>();

		private Harmony _harmony;

		private bool _runtimeActive;

		private bool _shutDown;

		internal static ManualLogSource Log { get; private set; }

		internal static TrulySmoothLaddersConfig Settings { get; private set; }

		internal static ICoreServices Core { get; private set; }

		private void Awake()
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Settings = TrulySmoothLaddersConfig.Load((BaseUnityPlugin)(object)this, Paths.ConfigPath, ((BaseUnityPlugin)this).Logger);
			try
			{
				if (!ModCoreApi.IsAvailable)
				{
					throw new InvalidOperationException("ModCore did not initialize before TrulySmoothLadders.");
				}
				Core = ModCoreApi.Services;
				RegisterWithCore();
				if (TryGetLegacyLadderOwner(out var version))
				{
					string text = "GeneralTweaks " + version?.ToString() + " still contains smooth ladders; TrulySmoothLadders is paused to prevent duplicate ramps.";
					Core.Modules.SetState(ModuleId, (ModuleRuntimeState)3, text);
					((BaseUnityPlugin)this).Logger.LogWarning((object)(text + " Update GeneralTweaks to 2.0.0 or newer."));
					return;
				}
				_harmony = new Harmony("jg224.TrulySmoothLadders");
				_harmony.PatchAll(Assembly.GetExecutingAssembly());
				_runtimeActive = true;
				Core.Modules.SetState(ModuleId, (ModuleRuntimeState)2, "Client-local smooth ladder ramps ready.");
				Game.isModded = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("TrulySmoothLadders 0.5.4 loaded; protocol " + 1 + "."));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("TrulySmoothLadders failed to initialize safely: " + ex));
				if (Core != null)
				{
					Core.Modules.SetState(ModuleId, (ModuleRuntimeState)5, ex.Message);
				}
				Shutdown();
				throw;
			}
		}

		private void Update()
		{
			if (_runtimeActive)
			{
				LadderRampRuntime.Tick();
			}
		}

		private void OnDestroy()
		{
			Shutdown();
		}

		private void RegisterWithCore()
		{
			//IL_0029: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0059: 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_00c2: Expected O, but got Unknown
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			SemanticVersion val = default(SemanticVersion);
			if (!SemanticVersion.TryParse("0.5.4", ref val))
			{
				throw new InvalidOperationException("Invalid plugin version.");
			}
			_registrations.Add(Core.Modules.Register(new ModuleDescriptor(ModuleId, "jg224.TrulySmoothLadders", "TrulySmoothLadders", val, 1, (ModuleSide)1, (ModuleRequirement)1, 0uL, 1, 1)));
			foreach (IDisposable item in Settings.RegisterMetadata(Core, ModuleId))
			{
				_registrations.Add(item);
			}
			_registrations.Add(Core.Compatibility.Register(new CompatibilityRule("jg224.TrulySmoothLadders", "BetterLaddersContinued", (CompatibilityRuleKind)2, (CompatibilitySeverity)2, "Both mods replace vanilla ladder movement; remove one implementation.", "ladders")));
			_registrations.Add(Core.Lifecycle.Subscribe(ModuleId, (LifecycleEventKind)9, (Action<LifecycleEvent>)delegate
			{
				LadderRampRuntime.DestroyAll();
			}, 0));
			_registrations.Add(Core.Lifecycle.Subscribe(ModuleId, (LifecycleEventKind)3, (Action<LifecycleEvent>)delegate
			{
				LadderRampRuntime.DestroyAll();
			}, 0));
		}

		private static bool TryGetLegacyLadderOwner(out Version version)
		{
			version = null;
			if (!Chainloader.PluginInfos.TryGetValue("JG224.GeneralTweaks", out var value))
			{
				return false;
			}
			version = value.Metadata.Version;
			if (version != null)
			{
				return version.CompareTo(new Version(2, 0, 0)) < 0;
			}
			return false;
		}

		private void Shutdown()
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			if (_shutDown)
			{
				return;
			}
			_shutDown = true;
			_runtimeActive = false;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
			LadderRampRuntime.DestroyAll();
			for (int num = _registrations.Count - 1; num >= 0; num--)
			{
				try
				{
					_registrations[num].Dispose();
				}
				catch (Exception ex)
				{
					ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
					if (logger != null)
					{
						logger.LogWarning((object)("Registration cleanup failed: " + ex.Message));
					}
				}
			}
			_registrations.Clear();
			if (Core != null)
			{
				Core.Metrics.RemoveOwner(ModuleId);
			}
			Core = null;
			Settings = null;
			Log = null;
		}
	}
	internal sealed class TrulySmoothLaddersConfig
	{
		private readonly ConfigFile _file;

		internal ConfigEntry<bool> BetterLaddersEnabled { get; }

		internal ConfigEntry<bool> AllowMonsterClimbing { get; }

		internal ConfigEntry<string> PrefabAdjustments { get; }

		internal ConfigEntry<bool> FitDiagnostics { get; }

		private TrulySmoothLaddersConfig(ConfigFile file)
		{
			_file = file;
			BetterLaddersEnabled = file.Bind<bool>("Movement", "BetterLadders", true, "Replace vanilla ladder auto-jump bouncing with smooth forward climbing.");
			AllowMonsterClimbing = file.Bind<bool>("Movement", "AllowMonsterClimbing", false, "Allow monsters to climb supported ladders. False blocks climbing; true restores normal climbing. Applies while BetterLadders is enabled, to monsters simulated by this client. Changes take effect immediately.");
			PrefabAdjustments = file.Bind<string>("Movement", "PrefabAdjustments", "", "Optional runtime prefab=width,normalOffset,endMargin in metres; e.g. wood_ladder=1.3,0.42,0.35. Width 0.5-3, offset 0-0.75, margin 0-1. Upper ends are capped at the highest tread plus 2 cm. Changes rebuild ramps on next contact.");
			FitDiagnostics = file.Bind<bool>("Diagnostics", "FitDiagnostics", false, "Log each successful ramp fit and rejected ladder once, including measured geometry. No per-frame success logs.");
		}

		internal static TrulySmoothLaddersConfig Load(BaseUnityPlugin plugin, string configDirectory, ManualLogSource log)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			if (!Directory.Exists(configDirectory))
			{
				Directory.CreateDirectory(configDirectory);
			}
			string text = LegacyLadderConfigRules.FindImportSource(Directory.GetFiles(configDirectory, "*.cfg", SearchOption.TopDirectoryOnly));
			string text2 = Path.Combine(configDirectory, "jg224.TrulySmoothLadders.cfg");
			TrulySmoothLaddersConfig trulySmoothLaddersConfig = new TrulySmoothLaddersConfig(PluginConfigFiles.Attach(plugin, new ConfigFile(text2, true, plugin.Info.Metadata)));
			if (text != null)
			{
				trulySmoothLaddersConfig.ImportLegacyGeneralTweaks(text, log);
			}
			return trulySmoothLaddersConfig;
		}

		internal IEnumerable<IDisposable> RegisterMetadata(ICoreServices services, ModuleId owner)
		{
			//IL_0016: 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)
			yield return services.Configuration.Register(new ConfigSettingDescriptor(owner, ((ConfigEntryBase)AllowMonsterClimbing).Definition.Section, ((ConfigEntryBase)AllowMonsterClimbing).Definition.Key, (ConfigScope)1, (Func<string>)(() => AllowMonsterClimbing.Value.ToString()), 1));
			yield return services.Configuration.Register(new ConfigSettingDescriptor(owner, ((ConfigEntryBase)PrefabAdjustments).Definition.Section, ((ConfigEntryBase)PrefabAdjustments).Definition.Key, (ConfigScope)1, (Func<string>)(() => PrefabAdjustments.Value), 1));
			yield return services.Configuration.Register(new ConfigSettingDescriptor(owner, ((ConfigEntryBase)FitDiagnostics).Definition.Section, ((ConfigEntryBase)FitDiagnostics).Definition.Key, (ConfigScope)5, (Func<string>)(() => FitDiagnostics.Value.ToString()), 1));
			yield return services.Configuration.Register(new ConfigSettingDescriptor(owner, ((ConfigEntryBase)BetterLaddersEnabled).Definition.Section, ((ConfigEntryBase)BetterLaddersEnabled).Definition.Key, (ConfigScope)1, (Func<string>)(() => Convert.ToString(((ConfigEntryBase)BetterLaddersEnabled).BoxedValue, CultureInfo.InvariantCulture)), 1));
		}

		private void ImportLegacyGeneralTweaks(string path, ManualLogSource log)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			try
			{
				ConfigFile val = new ConfigFile(path, false);
				BetterLaddersEnabled.Value = val.Bind<bool>("Movement", "BetterLadders", BetterLaddersEnabled.Value, (ConfigDescription)null).Value;
				_file.Save();
				if (log != null)
				{
					log.LogInfo((object)("Imported the BetterLadders setting from " + Path.GetFileName(path) + " into jg224.TrulySmoothLadders.cfg."));
				}
			}
			catch (Exception ex)
			{
				if (log != null)
				{
					log.LogWarning((object)("Could not import the GeneralTweaks BetterLadders setting; using the TrulySmoothLadders default. " + ex.Message));
				}
			}
		}
	}
}
namespace JG224.TrulySmoothLadders.Patches
{
	[HarmonyPatch(typeof(Character), "Awake")]
	internal static class BetterLaddersCharacterPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Character __instance)
		{
			TrulySmoothLaddersConfig settings = Plugin.Settings;
			if (settings != null && settings.BetterLaddersEnabled.Value)
			{
				LadderRampRuntime.OnCharacterInitialized(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Piece), "Awake")]
	internal static class BetterLaddersPiecePatch
	{
		[HarmonyPostfix]
		private static void Postfix(Piece __instance)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			TrulySmoothLaddersConfig settings = Plugin.Settings;
			if (settings == null || !settings.BetterLaddersEnabled.Value || !Object.op_Implicit((Object)(object)__instance) || !PrefabRules.IsVanillaLadder(((Object)((Component)__instance).gameObject).name))
			{
				return;
			}
			ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
			if (Object.op_Implicit((Object)(object)component) && component.IsValid())
			{
				AutoJumpLedge componentInChildren = ((Component)__instance).GetComponentInChildren<AutoJumpLedge>(true);
				if (Object.op_Implicit((Object)(object)componentInChildren))
				{
					LadderRampRuntime.Ensure(__instance, ((Component)componentInChildren).transform.forward);
				}
			}
		}
	}
	[HarmonyPatch(typeof(AutoJumpLedge), "OnTriggerStay")]
	internal static class BetterLaddersPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(AutoJumpLedge __instance, Collider collider)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: 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)
			TrulySmoothLaddersConfig settings = Plugin.Settings;
			if (settings == null || !settings.BetterLaddersEnabled.Value)
			{
				return true;
			}
			Character val = (Object.op_Implicit((Object)(object)collider) ? ((Component)collider).GetComponent<Character>() : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return true;
			}
			Piece componentInParent = ((Component)__instance).GetComponentInParent<Piece>();
			if (!Object.op_Implicit((Object)(object)componentInParent) || !PrefabRules.IsVanillaLadder(((Object)((Component)componentInParent).gameObject).name))
			{
				return true;
			}
			Vector3 forward = ((Component)__instance).transform.forward;
			if (!val.IsPlayer())
			{
				if (!val.IsOwner())
				{
					return true;
				}
				if (!LadderRampRuntime.Ensure(componentInParent, forward))
				{
					return true;
				}
				return !LadderRampRuntime.IsMonsterClimbBlocked(componentInParent);
			}
			if ((Object)(object)val != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			return LadderRampRules.ShouldUseVanillaJump(LadderRampRuntime.Ensure(componentInParent, forward));
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "UpdateTarget")]
	internal static class MonsterLadderAttackPatch
	{
		[HarmonyPostfix]
		private static void Postfix(MonsterAI __instance, Humanoid humanoid, Character ___m_targetCreature, ref StaticTarget ___m_targetStatic)
		{
			MonsterLadderAttackRuntime.UpdateTarget(__instance, humanoid, ___m_targetCreature, ref ___m_targetStatic);
		}
	}
	[HarmonyPatch(typeof(Character), "UpdateWalking")]
	internal static class MonsterLadderWalkingPatch
	{
		[HarmonyPrefix]
		private static void Prefix(Character __instance, ref Vector3 ___m_moveDir, out Vector3 __state)
		{
			//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_000e: 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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			__state = ___m_moveDir;
			___m_moveDir *= LadderRampRuntime.MonsterMovementMultiplier(__instance, ___m_moveDir);
		}

		[HarmonyFinalizer]
		private static void Finalizer(ref Vector3 ___m_moveDir, Vector3 __state)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			___m_moveDir = __state;
		}
	}
	[HarmonyPatch(typeof(Character), "UpdateBodyFriction")]
	internal static class BetterLaddersFrictionPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Character __instance, CapsuleCollider ___m_collider, Vector3 ___m_moveDir)
		{
			TrulySmoothLaddersConfig settings = Plugin.Settings;
			if (settings != null && settings.BetterLaddersEnabled.Value && Object.op_Implicit((Object)(object)Player.m_localPlayer) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && Object.op_Implicit((Object)(object)___m_collider))
			{
				bool interrupted = __instance.IsDead() || __instance.IsSwimming() || __instance.IsFlying() || __instance.IsDebugFlying() || __instance.IsAttached() || __instance.IsTeleporting() || __instance.InAttack() || __instance.InDodge() || __instance.IsKnockedBack();
				Collider lastGroundCollider = __instance.GetLastGroundCollider();
				bool ramp = LadderRampRuntime.IsRampCollider(lastGroundCollider) && lastGroundCollider.enabled && ((Component)lastGroundCollider).gameObject.activeInHierarchy;
				if (LadderFrictionRules.ShouldHoldPosition(__instance.IsOnGround(), ramp, interrupted, ((Vector3)(ref ___m_moveDir)).magnitude))
				{
					PhysicsMaterial material = ((Collider)___m_collider).material;
					material.staticFriction = Mathf.Max(material.staticFriction, 2f);
					material.dynamicFriction = Mathf.Max(material.dynamicFriction, 2f);
					material.frictionCombine = (PhysicsMaterialCombine)3;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Character), "GetSlideAngle")]
	internal static class BetterLaddersSlidePatch
	{
		private const float LadderSlideAngle = 55f;

		[HarmonyPostfix]
		private static void Postfix(Character __instance, ref float __result)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && LadderRampRuntime.IsRampCollider(__instance.GetLastGroundCollider()))
			{
				__result = Mathf.Max(__result, 55f);
			}
		}
	}
	internal static class LadderRampRuntime
	{
		private sealed class RampEntry
		{
			internal GameObject Root;

			internal Collider[] Colliders;

			internal Transform Ladder;

			internal Piece Piece;

			internal Vector3 LocalBottom;

			internal Vector3 LocalForward;

			internal float Span;

			internal float Rise;

			internal float Width;
		}

		private sealed class CharacterEntry
		{
			internal Character Owner;

			internal Collider[] Colliders;

			internal readonly List<RampEntry> NearbyRamps = new List<RampEntry>();
		}

		private sealed class Rejection
		{
			internal Piece Piece;

			internal float RetryAfter;
		}

		internal const string RampObjectName = "JG224_BetterLadderRamp";

		private const float RampThickness = 0.08f;

		private static readonly Dictionary<int, RampEntry> Ramps = new Dictionary<int, RampEntry>();

		private static readonly Dictionary<int, CharacterEntry> Isolated = new Dictionary<int, CharacterEntry>();

		private static readonly List<Character> Nearby = new List<Character>();

		private static readonly Dictionary<int, Rejection> Rejected = new Dictionary<int, Rejection>();

		private static readonly List<int> Stale = new List<int>();

		private static Dictionary<string, LadderAdjustment> _adjustments = LadderAdjustment.Parse("");

		private static string _raw;

		private static float _nextCleanup;

		internal static bool MonsterClimbingBlocked
		{
			get
			{
				TrulySmoothLaddersConfig settings = Plugin.Settings;
				if (settings != null && settings.BetterLaddersEnabled.Value)
				{
					return !Plugin.Settings.AllowMonsterClimbing.Value;
				}
				return false;
			}
		}

		internal static bool Ensure(Piece ladder, Vector3 ladderForward)
		{
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: 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_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0363: Unknown result type (might be due to invalid IL or missing references)
			//IL_0365: Unknown result type (might be due to invalid IL or missing references)
			//IL_0371: Unknown result type (might be due to invalid IL or missing references)
			//IL_0376: Unknown result type (might be due to invalid IL or missing references)
			//IL_037b: Unknown result type (might be due to invalid IL or missing references)
			//IL_037f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0384: Unknown result type (might be due to invalid IL or missing references)
			//IL_0386: Unknown result type (might be due to invalid IL or missing references)
			//IL_038b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0394: Unknown result type (might be due to invalid IL or missing references)
			//IL_0399: Unknown result type (might be due to invalid IL or missing references)
			//IL_039e: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0404: Unknown result type (might be due to invalid IL or missing references)
			//IL_0406: Unknown result type (might be due to invalid IL or missing references)
			//IL_040d: Unknown result type (might be due to invalid IL or missing references)
			//IL_041f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0424: Unknown result type (might be due to invalid IL or missing references)
			//IL_0428: 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_045e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0463: Unknown result type (might be due to invalid IL or missing references)
			//IL_0467: Unknown result type (might be due to invalid IL or missing references)
			//IL_046c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0471: Unknown result type (might be due to invalid IL or missing references)
			//IL_0494: Unknown result type (might be due to invalid IL or missing references)
			//IL_049b: Expected O, but got Unknown
			//IL_04b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_050e: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_05fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0600: Unknown result type (might be due to invalid IL or missing references)
			//IL_0605: Unknown result type (might be due to invalid IL or missing references)
			//IL_0609: Unknown result type (might be due to invalid IL or missing references)
			//IL_064c: Unknown result type (might be due to invalid IL or missing references)
			//IL_064e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0653: Unknown result type (might be due to invalid IL or missing references)
			//IL_065f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0661: Unknown result type (might be due to invalid IL or missing references)
			//IL_0666: Unknown result type (might be due to invalid IL or missing references)
			//IL_0679: Unknown result type (might be due to invalid IL or missing references)
			//IL_067e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0682: Unknown result type (might be due to invalid IL or missing references)
			//IL_068c: Unknown result type (might be due to invalid IL or missing references)
			//IL_053a: Unknown result type (might be due to invalid IL or missing references)
			//IL_053f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0547: Unknown result type (might be due to invalid IL or missing references)
			//IL_054f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0566: Unknown result type (might be due to invalid IL or missing references)
			//IL_056b: Unknown result type (might be due to invalid IL or missing references)
			//IL_056f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0574: Unknown result type (might be due to invalid IL or missing references)
			//IL_0579: Unknown result type (might be due to invalid IL or missing references)
			//IL_058f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0595: Unknown result type (might be due to invalid IL or missing references)
			//IL_059c: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02df: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0302: Unknown result type (might be due to invalid IL or missing references)
			//IL_0306: Unknown result type (might be due to invalid IL or missing references)
			//IL_030b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0318: Unknown result type (might be due to invalid IL or missing references)
			//IL_031d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0321: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)ladder))
			{
				TrulySmoothLaddersConfig settings = Plugin.Settings;
				if (settings != null && settings.BetterLaddersEnabled.Value && PrefabRules.IsVanillaLadder(((Object)((Component)ladder).gameObject).name))
				{
					RefreshSettings();
					int instanceID = ((Object)ladder).GetInstanceID();
					if (Ramps.TryGetValue(instanceID, out var value) && Object.op_Implicit((Object)(object)value.Root))
					{
						return true;
					}
					if (Rejected.TryGetValue(instanceID, out var value2) && (Object)(object)value2.Piece == (Object)(object)ladder && Time.unscaledTime < value2.RetryAfter)
					{
						return false;
					}
					string text = ((Object)((Component)ladder).gameObject).name.Replace("(Clone)", "").Trim();
					if (!_adjustments.TryGetValue(text, out var value3))
					{
						value3 = LadderAdjustment.Default;
					}
					Vector3 val = default(Vector3);
					((Vector3)(ref val))..ctor(ladderForward.x, 0f, ladderForward.z);
					if (((Vector3)(ref val)).sqrMagnitude < 0.01f)
					{
						return Reject(ladder, "ladder forward direction is vertical");
					}
					((Vector3)(ref val)).Normalize();
					Vector3 val2 = Vector3.Cross(Vector3.up, val);
					Vector3 normalized = ((Vector3)(ref val2)).normalized;
					Collider[] componentsInChildren = ((Component)ladder).GetComponentsInChildren<Collider>(true);
					Collider val3 = null;
					Collider val4 = null;
					float num = float.MaxValue;
					float num2 = float.MinValue;
					float num3 = 0f;
					int num4 = 0;
					int layer = ((Component)ladder).gameObject.layer;
					Bounds bounds;
					foreach (Collider val5 in componentsInChildren)
					{
						if (!Object.op_Implicit((Object)(object)val5) || val5.isTrigger || ((Object)((Component)val5).gameObject).name == "JG224_BetterLadderRamp")
						{
							continue;
						}
						layer = ((Component)val5).gameObject.layer;
						bounds = val5.bounds;
						Vector3 size = ((Bounds)(ref bounds)).size;
						if (LadderColliderRules.IsStepSurface(size.x, size.y, size.z))
						{
							bounds = val5.bounds;
							float num5 = Vector3.Dot(((Bounds)(ref bounds)).center, val);
							float num6 = num3;
							bounds = val5.bounds;
							num3 = num6 + Vector3.Dot(((Bounds)(ref bounds)).center, normalized);
							num4++;
							if (num5 < num)
							{
								num = num5;
								val3 = val5;
							}
							if (num5 > num2)
							{
								num2 = num5;
								val4 = val5;
							}
						}
					}
					if (num4 >= 2 && Object.op_Implicit((Object)(object)val3) && Object.op_Implicit((Object)(object)val4))
					{
						float lowerForward = num;
						bounds = val3.bounds;
						float y = ((Bounds)(ref bounds)).max.y;
						float upperForward = num2;
						bounds = val4.bounds;
						if (LadderRampRules.TryCreateProfile(lowerForward, y, upperForward, ((Bounds)(ref bounds)).max.y, out var profile))
						{
							float num7 = float.MinValue;
							foreach (Collider val6 in componentsInChildren)
							{
								if (Object.op_Implicit((Object)(object)val6) && !val6.isTrigger && !(((Object)((Component)val6).gameObject).name == "JG224_BetterLadderRamp"))
								{
									bounds = val6.bounds;
									Vector3 size2 = ((Bounds)(ref bounds)).size;
									if (LadderColliderRules.IsStepSurface(size2.x, size2.y, size2.z))
									{
										bounds = val6.bounds;
										float num8 = Vector3.Dot(((Bounds)(ref bounds)).center, val);
										float num9 = num7;
										bounds = val6.bounds;
										num7 = Mathf.Max(num9, ((Bounds)(ref bounds)).max.y - profile.Slope * num8);
									}
								}
							}
							float num10 = (num + num2) * 0.5f;
							float num11 = num3 / (float)num4;
							val2 = val + Vector3.up * profile.Slope;
							Vector3 normalized2 = ((Vector3)(ref val2)).normalized;
							val2 = Vector3.up - val * profile.Slope;
							Vector3 normalized3 = ((Vector3)(ref val2)).normalized;
							Vector3 val7 = val * num10 + normalized * num11;
							val7.y = profile.Slope * num10 + num7;
							Vector3 val8 = val7 + normalized3 * value3.Offset;
							float length = profile.SurfaceLength + value3.Margin * 2f;
							float centerForward = Vector3.Dot(val8, val);
							float y2 = val8.y;
							float slope = profile.Slope;
							bounds = val4.bounds;
							if (!LadderRampRules.TryCreateLanding(centerForward, y2, slope, length, ((Bounds)(ref bounds)).max.y, out var landing))
							{
								return Reject(ladder, "ramp cannot meet the upper tread safely");
							}
							val8 = val * ((landing.LowerForward + landing.UpperForward) * 0.5f) + normalized * num11;
							val8.y = (landing.LowerHeight + landing.UpperHeight) * 0.5f;
							GameObject val9 = new GameObject("JG224_BetterLadderRamp");
							((Object)val9).hideFlags = (HideFlags)61;
							val9.layer = layer;
							val9.transform.position = val8 - normalized3 * 0.04f;
							val9.transform.rotation = Quaternion.LookRotation(normalized2, normalized3);
							val9.transform.SetParent(((Component)ladder).transform, true);
							val9.AddComponent<BoxCollider>().size = new Vector3(value3.Width, 0.08f, landing.InclineLength);
							float num12 = landing.CapEndForward - landing.UpperForward;
							if (num12 > 0.001f)
							{
								GameObject val10 = new GameObject("JG224_BetterLadderRamp")
								{
									hideFlags = (HideFlags)61,
									layer = layer
								};
								Vector3 position = val * ((landing.UpperForward + landing.CapEndForward) * 0.5f) + normalized * num11;
								position.y = landing.UpperHeight - 0.04f;
								val10.transform.position = position;
								val10.transform.rotation = Quaternion.LookRotation(val, Vector3.up);
								val10.transform.SetParent(val9.transform, true);
								val10.AddComponent<BoxCollider>().size = new Vector3(value3.Width, 0.08f, num12);
							}
							Vector3 val11 = val * num + normalized * num11;
							bounds = val3.bounds;
							val11.y = ((Bounds)(ref bounds)).max.y;
							RampEntry obj = new RampEntry
							{
								Root = val9,
								Colliders = CollectColliders(val9),
								Ladder = ((Component)ladder).transform,
								Piece = ladder,
								LocalBottom = ((Component)ladder).transform.InverseTransformPoint(val11),
								LocalForward = ((Component)ladder).transform.InverseTransformDirection(val),
								Span = num2 - num
							};
							bounds = val4.bounds;
							obj.Rise = ((Bounds)(ref bounds)).max.y - val11.y;
							obj.Width = value3.Width;
							RampEntry rampEntry = obj;
							Ramps[instanceID] = rampEntry;
							Rejected.Remove(instanceID);
							IsolateRamp(rampEntry);
							if (Plugin.Settings.FitDiagnostics.Value)
							{
								ManualLogSource log = Plugin.Log;
								if (log != null)
								{
									log.LogInfo((object)("Ladder fit " + text + ": steps=" + num4 + ", slope=" + profile.Slope + ", inclineLength=" + landing.InclineLength + ", landingLength=" + num12 + ", landingHeight=" + landing.UpperHeight));
								}
							}
							return true;
						}
					}
					return Reject(ladder, "fewer than two suitable steps or unsupported slope/span");
				}
			}
			return false;
		}

		private static bool Reject(Piece ladder, string reason)
		{
			int instanceID = ((Object)ladder).GetInstanceID();
			if (!Rejected.TryGetValue(instanceID, out var value) || (Object)(object)value.Piece != (Object)(object)ladder)
			{
				value = new Rejection
				{
					Piece = ladder
				};
				Rejected[instanceID] = value;
				TrulySmoothLaddersConfig settings = Plugin.Settings;
				if (settings != null && settings.FitDiagnostics.Value)
				{
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogWarning((object)("Ladder fit rejected for " + ((Object)ladder).name + ": " + reason + "; vanilla auto-jump retained."));
					}
				}
			}
			value.RetryAfter = Time.unscaledTime + 2f;
			return false;
		}

		private static void RefreshSettings()
		{
			string text = Plugin.Settings?.PrefabAdjustments.Value ?? "";
			if (!(_raw == text))
			{
				DestroyAll();
				_raw = text;
				_adjustments = LadderAdjustment.Parse(text);
			}
		}

		internal static void Tick()
		{
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			TrulySmoothLaddersConfig settings = Plugin.Settings;
			if (settings == null || !settings.BetterLaddersEnabled.Value)
			{
				DestroyAll();
				return;
			}
			RefreshSettings();
			if (!MonsterClimbingBlocked)
			{
				MonsterLadderAttackRuntime.DestroyAll();
			}
			if (Time.unscaledTime < _nextCleanup)
			{
				return;
			}
			_nextCleanup = Time.unscaledTime + 1f;
			MonsterLadderAttackRuntime.Sweep();
			Stale.Clear();
			foreach (KeyValuePair<int, RampEntry> ramp in Ramps)
			{
				if (!Object.op_Implicit((Object)(object)ramp.Value.Root))
				{
					Stale.Add(ramp.Key);
				}
			}
			for (int i = 0; i < Stale.Count; i++)
			{
				Ramps.Remove(Stale[i]);
			}
			Stale.Clear();
			foreach (KeyValuePair<int, Rejection> item in Rejected)
			{
				if (!Object.op_Implicit((Object)(object)item.Value.Piece))
				{
					Stale.Add(item.Key);
				}
			}
			for (int j = 0; j < Stale.Count; j++)
			{
				Rejected.Remove(Stale[j]);
			}
			Stale.Clear();
			foreach (KeyValuePair<int, CharacterEntry> item2 in Isolated)
			{
				if ((Object)(object)item2.Value.Owner == (Object)null || !Object.op_Implicit((Object)(object)item2.Value.Owner))
				{
					Stale.Add(item2.Key);
				}
			}
			for (int k = 0; k < Stale.Count; k++)
			{
				Isolated.Remove(Stale[k]);
			}
			Stale.Clear();
			foreach (CharacterEntry value in Isolated.Values)
			{
				value.NearbyRamps.Clear();
			}
			foreach (RampEntry value2 in Ramps.Values)
			{
				if (Object.op_Implicit((Object)(object)value2.Root))
				{
					Nearby.Clear();
					Character.GetCharactersInRange(value2.Root.transform.position, 30f, Nearby);
					for (int l = 0; l < Nearby.Count; l++)
					{
						IsolateCharacter(Nearby[l], value2);
					}
					Nearby.Clear();
				}
			}
		}

		internal static void OnCharacterInitialized(Character character)
		{
			TrulySmoothLaddersConfig settings = Plugin.Settings;
			if (settings == null || !settings.BetterLaddersEnabled.Value || !Object.op_Implicit((Object)(object)character) || Ramps.Count == 0 || !LadderIsolationRules.ShouldIsolateCharacter(character.IsPlayer()))
			{
				return;
			}
			foreach (RampEntry value in Ramps.Values)
			{
				IsolateCharacter(character, value);
			}
		}

		private static void IsolateRamp(RampEntry ramp)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)ramp.Root))
			{
				Nearby.Clear();
				Character.GetCharactersInRange(ramp.Root.transform.position, 30f, Nearby);
				for (int i = 0; i < Nearby.Count; i++)
				{
					IsolateCharacter(Nearby[i], ramp);
				}
				Nearby.Clear();
			}
		}

		private static void IsolateCharacter(Character character, RampEntry ramp)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)character) || !Object.op_Implicit((Object)(object)ramp.Root) || !LadderIsolationRules.ShouldIsolateCharacter(character.IsPlayer()))
			{
				return;
			}
			Collider[] characterColliders = GetCharacterColliders(character);
			CharacterEntry characterEntry = Isolated[((Object)character).GetInstanceID()];
			Vector3 val = ((Component)character).transform.position - ramp.Root.transform.position;
			if (((Vector3)(ref val)).sqrMagnitude <= 900f && !characterEntry.NearbyRamps.Contains(ramp))
			{
				characterEntry.NearbyRamps.Add(ramp);
			}
			foreach (Collider val2 in characterColliders)
			{
				if (!ShouldUse(val2, character))
				{
					continue;
				}
				for (int j = 0; j < ramp.Colliders.Length; j++)
				{
					Collider val3 = ramp.Colliders[j];
					if (ShouldUse(val3, null))
					{
						Physics.IgnoreCollision(val2, val3, true);
					}
				}
			}
		}

		private static bool ShouldUse(Collider collider, Character owner)
		{
			if (Object.op_Implicit((Object)(object)collider))
			{
				return LadderIsolationRules.ShouldIsolateCollider(collider.isTrigger, collider.enabled, ((Component)collider).gameObject.activeInHierarchy, (Object)(object)owner == (Object)null || (Object)(object)((Component)collider).GetComponentInParent<Character>() == (Object)(object)owner);
			}
			return false;
		}

		private static Collider[] GetCharacterColliders(Character character)
		{
			int instanceID = ((Object)character).GetInstanceID();
			if (Isolated.TryGetValue(instanceID, out var value) && (Object)(object)value.Owner == (Object)(object)character && value.Colliders != null && value.Colliders.Length != 0)
			{
				return value.Colliders;
			}
			List<Collider> list = new List<Collider>();
			AddCollider(list, character, (Collider)(object)character.GetCollider());
			Collider[] componentsInChildren = ((Component)character).GetComponentsInChildren<Collider>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				AddCollider(list, character, componentsInChildren[i]);
			}
			Collider[] array = list.ToArray();
			Isolated[instanceID] = new CharacterEntry
			{
				Owner = character,
				Colliders = array
			};
			return array;
		}

		private static void AddCollider(List<Collider> colliders, Character owner, Collider collider)
		{
			if (Object.op_Implicit((Object)(object)collider) && !collider.isTrigger && !(((Object)((Component)collider).gameObject).name == "JG224_BetterLadderRamp") && !((Object)(object)((Component)collider).GetComponentInParent<Character>() != (Object)(object)owner) && !colliders.Contains(collider))
			{
				colliders.Add(collider);
			}
		}

		private static Collider[] CollectColliders(GameObject ramp)
		{
			Collider[] componentsInChildren = ramp.GetComponentsInChildren<Collider>(true);
			List<Collider> list = new List<Collider>(componentsInChildren.Length);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				if (Object.op_Implicit((Object)(object)componentsInChildren[i]) && !componentsInChildren[i].isTrigger)
				{
					list.Add(componentsInChildren[i]);
				}
			}
			return list.ToArray();
		}

		internal static bool IsRampCollider(Collider collider)
		{
			if (Object.op_Implicit((Object)(object)collider))
			{
				return ((Object)((Component)collider).gameObject).name == "JG224_BetterLadderRamp";
			}
			return false;
		}

		internal static bool IsMonsterClimbBlocked(Piece ladder)
		{
			if (MonsterClimbingBlocked && Object.op_Implicit((Object)(object)ladder) && Ramps.TryGetValue(((Object)ladder).GetInstanceID(), out var value) && Object.op_Implicit((Object)(object)value.Root))
			{
				return value.Root.activeInHierarchy;
			}
			return false;
		}

		internal static bool IsAtLadder(Character character, Piece ladder)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)character) || !IsMonsterClimbBlocked(ladder) || !Object.op_Implicit((Object)(object)character.GetCollider()))
			{
				return false;
			}
			RampEntry rampEntry = Ramps[((Object)ladder).GetInstanceID()];
			Vector3 val = rampEntry.Ladder.TransformDirection(rampEntry.LocalForward);
			Vector3 val2 = ((Component)character).transform.position - rampEntry.Ladder.TransformPoint(rampEntry.LocalBottom);
			Bounds bounds = ((Collider)character.GetCollider()).bounds;
			Vector3 extents = ((Bounds)(ref bounds)).extents;
			return MonsterLadderRules.IsInClimbArea(Vector3.Dot(val2, val), Vector3.Dot(val2, Vector3.Cross(Vector3.up, val)), val2.y, rampEntry.Span, rampEntry.Rise, rampEntry.Width, Mathf.Max(extents.x, extents.z));
		}

		internal static bool IsPursuitBeyondLadder(Character character, Character enemy, Piece ladder)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)character) || !Object.op_Implicit((Object)(object)enemy) || !IsMonsterClimbBlocked(ladder))
			{
				return false;
			}
			RampEntry rampEntry = Ramps[((Object)ladder).GetInstanceID()];
			Vector3 val = rampEntry.Ladder.TransformDirection(rampEntry.LocalForward);
			Vector3 val2 = rampEntry.Ladder.TransformPoint(rampEntry.LocalBottom);
			return MonsterLadderRules.IsPursuitBeyondLadder(Vector3.Dot(((Component)character).transform.position - val2, val), Vector3.Dot(((Component)enemy).transform.position - val2, val), ((Component)character).transform.position.y - val2.y, ((Component)enemy).transform.position.y - val2.y);
		}

		internal static float MonsterMovementMultiplier(Character character, Vector3 movement)
		{
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: 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)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)character) || character.IsPlayer())
			{
				return 1f;
			}
			int instanceID = ((Object)character).GetInstanceID();
			if (!MonsterClimbingBlocked || !character.IsOwner() || character.IsDead() || character.IsSwimming() || character.IsFlying() || character.IsDebugFlying() || character.IsAttached() || character.IsTeleporting() || character.InAttack() || character.InDodge() || character.IsKnockedBack())
			{
				return 1f;
			}
			CapsuleCollider collider = character.GetCollider();
			if (!Object.op_Implicit((Object)(object)collider) || !((Collider)collider).enabled || !((Component)collider).gameObject.activeInHierarchy)
			{
				return 1f;
			}
			Vector3 position = ((Component)character).transform.position;
			Bounds bounds = ((Collider)collider).bounds;
			float x = ((Bounds)(ref bounds)).extents.x;
			bounds = ((Collider)collider).bounds;
			float radius = Mathf.Max(x, ((Bounds)(ref bounds)).extents.z);
			if (!Isolated.TryGetValue(instanceID, out var value) || (Object)(object)value.Owner != (Object)(object)character)
			{
				return 1f;
			}
			foreach (RampEntry nearbyRamp in value.NearbyRamps)
			{
				if (!Object.op_Implicit((Object)(object)nearbyRamp.Root) || !nearbyRamp.Root.activeInHierarchy || !Object.op_Implicit((Object)(object)nearbyRamp.Ladder))
				{
					continue;
				}
				Vector3 val = nearbyRamp.Ladder.TransformDirection(nearbyRamp.LocalForward);
				Vector3 val2 = position - nearbyRamp.Ladder.TransformPoint(nearbyRamp.LocalBottom);
				float num = Vector3.Dot(val2, val);
				float across = Vector3.Dot(val2, Vector3.Cross(Vector3.up, val));
				if (MonsterLadderRules.IsInClimbArea(num, across, val2.y, nearbyRamp.Span, nearbyRamp.Rise, nearbyRamp.Width, radius) && MonsterLadderRules.IsAscending(Vector3.Dot(movement, val)))
				{
					Piece piece = nearbyRamp.Piece;
					if (num < 0f && MonsterLadderAttackRuntime.IsAttacking(character, piece))
					{
						return 1f;
					}
					MonsterLadderAttackRuntime.ObserveBlock(character, piece);
					return 0f;
				}
			}
			return 1f;
		}

		internal static void DestroyAll()
		{
			MonsterLadderAttackRuntime.DestroyAll();
			foreach (RampEntry value in Ramps.Values)
			{
				if (Object.op_Implicit((Object)(object)value.Root))
				{
					value.Root.SetActive(false);
					Object.Destroy((Object)(object)value.Root);
				}
			}
			Ramps.Clear();
			Rejected.Clear();
			Isolated.Clear();
			Nearby.Clear();
			Stale.Clear();
			_nextCleanup = 0f;
		}
	}
	internal static class MonsterLadderAttackRuntime
	{
		private sealed class Entry
		{
			internal Character Character;

			internal MonsterAI AI;

			internal Piece Ladder;

			internal float BlockedAt;

			internal float UpdatedAt;

			internal StaticTarget Assigned;

			internal StaticTarget Previous;

			internal Character Pursuit;
		}

		private static readonly Dictionary<int, Entry> Entries = new Dictionary<int, Entry>();

		private static readonly List<StaticTarget> CreatedTargets = new List<StaticTarget>();

		private static readonly List<int> Stale = new List<int>();

		private static readonly FieldRef<MonsterAI, StaticTarget> TargetField = AccessTools.FieldRefAccess<MonsterAI, StaticTarget>("m_targetStatic");

		internal static void ObserveBlock(Character character, Piece ladder)
		{
			BaseAI baseAI = character.GetBaseAI();
			MonsterAI val = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null);
			if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)ladder) || character.IsTamed() || !val.m_attackPlayerObjects || !((BaseAI)val).IsAlerted())
			{
				return;
			}
			int instanceID = ((Object)character).GetInstanceID();
			if (Entries.TryGetValue(instanceID, out var value))
			{
				if ((Object)(object)value.Character == (Object)(object)character && (Object)(object)value.Ladder == (Object)(object)ladder)
				{
					value.BlockedAt = Time.time;
					return;
				}
				if (Object.op_Implicit((Object)(object)value.Assigned) && LadderRampRuntime.IsAtLadder(character, value.Ladder))
				{
					return;
				}
				Release(value);
			}
			Entries[instanceID] = new Entry
			{
				Character = character,
				AI = val,
				Ladder = ladder,
				BlockedAt = Time.time,
				UpdatedAt = Time.time
			};
		}

		internal static bool IsAttacking(Character character, Piece ladder)
		{
			if (Object.op_Implicit((Object)(object)character) && Object.op_Implicit((Object)(object)ladder) && Entries.TryGetValue(((Object)character).GetInstanceID(), out var value) && (Object)(object)value.Character == (Object)(object)character && (Object)(object)value.Ladder == (Object)(object)ladder && Object.op_Implicit((Object)(object)value.Assigned) && Object.op_Implicit((Object)(object)value.AI))
			{
				return (Object)(object)TargetField.Invoke(value.AI) == (Object)(object)value.Assigned;
			}
			return false;
		}

		internal static void UpdateTarget(MonsterAI ai, Humanoid character, Character enemy, ref StaticTarget target)
		{
			if (!Object.op_Implicit((Object)(object)character) || !Entries.TryGetValue(((Object)character).GetInstanceID(), out var value))
			{
				return;
			}
			int instanceID = ((Object)character).GetInstanceID();
			bool pursuingPlayer = Object.op_Implicit((Object)(object)enemy) && enemy.IsPlayer() && !enemy.IsDead() && BaseAI.IsEnemy((Character)(object)character, enemy);
			if ((Object)(object)value.Character != (Object)(object)character || (Object)(object)value.AI != (Object)(object)ai || !((Character)character).IsOwner() || !LadderRampRuntime.MonsterClimbingBlocked || ((Character)character).IsPlayer() || ((Character)character).IsDead() || ((Character)character).m_aiCannotTargetOthers || ((Character)character).IsSwimming() || ((Character)character).IsFlying() || ((Character)character).IsDebugFlying() || ((Character)character).IsAttached() || ((Character)character).IsTeleporting() || !Object.op_Implicit((Object)(object)ZoneSystem.instance) || !MonsterLadderRules.CanAttackObstruction(ai.m_attackPlayerObjects, ((Character)character).IsTamed(), ((BaseAI)ai).IsAlerted(), !((BaseAI)ai).m_aggravatable || ((BaseAI)ai).IsAggravated(), ZoneSystem.instance.GetGlobalKey((GlobalKeys)30), pursuingPlayer) || !LadderRampRuntime.IsAtLadder((Character)(object)character, value.Ladder) || !LadderRampRuntime.IsPursuitBeyondLadder((Character)(object)character, enemy, value.Ladder) || (Object.op_Implicit((Object)(object)value.Assigned) && (Object)(object)value.Pursuit != (Object)(object)enemy) || (!Object.op_Implicit((Object)(object)value.Assigned) && !MonsterLadderRules.IsRecentBlock(Time.time, value.BlockedAt)))
			{
				Release(value);
				Entries.Remove(instanceID);
				return;
			}
			value.UpdatedAt = Time.time;
			if (((Character)character).InAttack() || ((Character)character).InDodge() || ((Character)character).IsKnockedBack())
			{
				return;
			}
			Piece ladder = value.Ladder;
			ZNetView component = ((Component)ladder).GetComponent<ZNetView>();
			WearNTear component2 = ((Component)ladder).GetComponent<WearNTear>();
			if (!ladder.IsPlacedByPlayer() || !Object.op_Implicit((Object)(object)component) || !component.IsValid() || !Object.op_Implicit((Object)(object)component2) || component2.GetHealthPercentage() <= 0f)
			{
				Release(value);
				Entries.Remove(instanceID);
				return;
			}
			if (!Object.op_Implicit((Object)(object)value.Assigned))
			{
				StaticTarget val = ((Component)ladder).GetComponent<StaticTarget>();
				if (!Object.op_Implicit((Object)(object)val))
				{
					val = (StaticTarget)(object)((Component)ladder).gameObject.AddComponent<LadderObstructionTarget>();
					val.m_primaryTarget = false;
					val.m_randomTarget = false;
					CreatedTargets.Add(val);
				}
				List<Collider> allColliders = val.GetAllColliders();
				Collider[] componentsInChildren = ((Component)ladder).GetComponentsInChildren<Collider>();
				foreach (Collider val2 in componentsInChildren)
				{
					if (Object.op_Implicit((Object)(object)val2) && val2.enabled && ((Component)val2).gameObject.activeInHierarchy && !val2.isTrigger && !allColliders.Contains(val2))
					{
						allColliders.Add(val2);
					}
				}
				value.Assigned = val;
				value.Previous = target;
				value.Pursuit = enemy;
			}
			target = value.Assigned;
		}

		private static void Release(Entry entry)
		{
			if (Object.op_Implicit((Object)(object)entry.AI) && Object.op_Implicit((Object)(object)entry.Assigned) && (Object)(object)TargetField.Invoke(entry.AI) == (Object)(object)entry.Assigned)
			{
				TargetField.Invoke(entry.AI) = (Object.op_Implicit((Object)(object)entry.Previous) ? entry.Previous : null);
			}
			entry.Assigned = null;
		}

		internal static void Sweep()
		{
			Stale.Clear();
			foreach (KeyValuePair<int, Entry> entry in Entries)
			{
				if (!Object.op_Implicit((Object)(object)entry.Value.Character) || !Object.op_Implicit((Object)(object)entry.Value.Ladder) || !entry.Value.Character.IsOwner() || Time.time - entry.Value.UpdatedAt > 2f)
				{
					Release(entry.Value);
					Stale.Add(entry.Key);
				}
			}
			foreach (int item in Stale)
			{
				Entries.Remove(item);
			}
			Stale.Clear();
			CreatedTargets.RemoveAll((StaticTarget target) => !Object.op_Implicit((Object)(object)target));
		}

		internal static void DestroyAll()
		{
			foreach (Entry value in Entries.Values)
			{
				Release(value);
			}
			Entries.Clear();
			foreach (StaticTarget createdTarget in CreatedTargets)
			{
				if (Object.op_Implicit((Object)(object)createdTarget))
				{
					((Behaviour)createdTarget).enabled = false;
					Object.Destroy((Object)(object)createdTarget);
				}
			}
			CreatedTargets.Clear();
			Stale.Clear();
		}
	}
	internal sealed class LadderObstructionTarget : StaticTarget
	{
		public override bool IsPriorityTarget()
		{
			return false;
		}

		public override bool IsRandomTarget()
		{
			return false;
		}
	}
}
namespace JG224.TrulySmoothLadders.Core
{
	internal readonly struct LadderAdjustment
	{
		internal float Width { get; }

		internal float Offset { get; }

		internal float Margin { get; }

		internal static LadderAdjustment Default => new LadderAdjustment(1.3f, 0.42f, 0.35f);

		internal LadderAdjustment(float width, float offset, float margin)
		{
			Width = width;
			Offset = offset;
			Margin = margin;
		}

		internal static Dictionary<string, LadderAdjustment> Parse(string raw)
		{
			Dictionary<string, LadderAdjustment> dictionary = new Dictionary<string, LadderAdjustment>(StringComparer.OrdinalIgnoreCase);
			string[] array = (raw ?? "").Split(new char[1] { ';' });
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(new char[1] { '=' });
				if (array2.Length == 2 && PrefabRules.IsVanillaLadder(array2[0].Trim()))
				{
					string[] array3 = array2[1].Split(new char[1] { ',' });
					if (array3.Length == 3 && Number(array3[0], 0.5f, 3f, out var number) && Number(array3[1], 0f, 0.75f, out var number2) && Number(array3[2], 0f, 1f, out var number3))
					{
						dictionary[array2[0].Trim()] = new LadderAdjustment(number, number2, number3);
					}
				}
			}
			return dictionary;
		}

		private static bool Number(string value, float min, float max, out float number)
		{
			if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out number) && !float.IsNaN(number) && !float.IsInfinity(number) && number >= min)
			{
				return number <= max;
			}
			return false;
		}
	}
	internal static class MonsterLadderRules
	{
		internal static bool CanAttackObstruction(bool attacksStructures, bool tamed, bool alerted, bool aggressionAllowed, bool passiveWorld, bool pursuingPlayer)
		{
			return attacksStructures && !tamed && alerted && aggressionAllowed && !passiveWorld && pursuingPlayer;
		}

		internal static bool IsRecentBlock(float now, float blockedAt)
		{
			if (now - blockedAt >= 0f)
			{
				return now - blockedAt <= 0.5f;
			}
			return false;
		}

		internal static bool IsPursuitBeyondLadder(float actorAlong, float enemyAlong, float actorHeight, float enemyHeight)
		{
			if (enemyAlong >= 0f && enemyAlong > actorAlong + 0.25f && !float.IsInfinity(enemyAlong) && enemyHeight > Math.Max(0f, actorHeight) + 0.25f)
			{
				return !float.IsInfinity(enemyHeight);
			}
			return false;
		}

		internal static bool IsInClimbArea(float forward, float across, float height, float span, float rise, float width, float radius)
		{
			if (forward >= 0f - radius - 0.5f && forward <= span && Math.Abs(across) <= width * 0.5f + radius && span > 0f && rise > 0f && radius >= 0f && width > 0f && height >= Math.Max(0f, forward) * rise / span - 0.75f)
			{
				return height <= Math.Max(0f, forward) * rise / span + 0.5f;
			}
			return false;
		}

		internal static bool IsAscending(float forwardInput)
		{
			if (forwardInput > 0f)
			{
				return !float.IsInfinity(forwardInput);
			}
			return false;
		}
	}
	internal static class LadderFrictionRules
	{
		internal const float MinimumIdleFriction = 2f;

		internal static bool ShouldHoldPosition(bool grounded, bool ramp, bool interrupted, float movementMagnitude)
		{
			if (grounded && ramp && !interrupted && movementMagnitude >= 0f)
			{
				return movementMagnitude < 0.1f;
			}
			return false;
		}
	}
	internal static class LadderColliderRules
	{
		internal static bool IsStepSurface(float width, float height, float depth)
		{
			if (!IsFinitePositive(width) || !IsFinitePositive(height) || !IsFinitePositive(depth))
			{
				return false;
			}
			float num = Math.Max(width, depth);
			if (height <= 0.35f)
			{
				return height <= num * 0.5f;
			}
			return false;
		}

		private static bool IsFinitePositive(float value)
		{
			if (value > 0f)
			{
				return IsFinite(value);
			}
			return false;
		}

		private static bool IsFinite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
	internal static class LadderIsolationRules
	{
		internal const float IsolationRadius = 30f;

		internal static bool ShouldIsolateCharacter(bool isPlayer)
		{
			return !isPlayer;
		}

		internal static bool ShouldIsolateCollider(bool isTrigger, bool enabled, bool activeInHierarchy, bool ownedByIsolatedCharacter)
		{
			return !isTrigger && enabled && activeInHierarchy && ownedByIsolatedCharacter;
		}
	}
	internal readonly struct LadderRampProfile
	{
		internal float Slope { get; }

		internal float HorizontalSpan { get; }

		internal float SurfaceLength => HorizontalSpan * (float)Math.Sqrt(1f + Slope * Slope);

		internal LadderRampProfile(float slope, float horizontalSpan)
		{
			Slope = slope;
			HorizontalSpan = horizontalSpan;
		}
	}
	internal static class LadderRampRules
	{
		internal const float LandingClearance = 0.02f;

		internal static bool TryCreateLanding(float centerForward, float centerHeight, float slope, float length, float highestTread, out LadderLanding landing)
		{
			landing = default(LadderLanding);
			if (!IsFinite(centerForward) || !IsFinite(centerHeight) || !IsFinite(slope) || !IsFinite(length) || !IsFinite(highestTread) || slope <= 0f || length <= 0f)
			{
				return false;
			}
			float num = (float)Math.Sqrt(1f + slope * slope);
			float num2 = length / (2f * num);
			float num3 = num2 * slope;
			float num4 = centerHeight - num3;
			float num5 = Math.Min(centerHeight + num3, highestTread + 0.02f);
			if (num5 - num4 < 0.01f)
			{
				return false;
			}
			float num6 = centerForward - num2;
			float num7 = num6 + (num5 - num4) / slope;
			landing = new LadderLanding(num6, num4, num7, num5, Math.Max(num7, centerForward + num2));
			return true;
		}

		internal static bool ShouldUseVanillaJump(bool rampReady)
		{
			return !rampReady;
		}

		internal static bool TryCreateProfile(float lowerForward, float lowerTop, float upperForward, float upperTop, out LadderRampProfile profile)
		{
			profile = default(LadderRampProfile);
			if (!IsFinite(lowerForward) || !IsFinite(lowerTop) || !IsFinite(upperForward) || !IsFinite(upperTop))
			{
				return false;
			}
			float num = upperForward - lowerForward;
			float num2 = upperTop - lowerTop;
			if (num < 0.25f || num2 < 0.1f)
			{
				return false;
			}
			float num3 = num2 / num;
			if (num3 < 0.4f || num3 > 2.5f)
			{
				return false;
			}
			float slope = Math.Min(num3, 1.35f);
			profile = new LadderRampProfile(slope, num);
			return true;
		}

		private static bool IsFinite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
	internal readonly struct LadderLanding
	{
		internal float LowerForward { get; }

		internal float LowerHeight { get; }

		internal float UpperForward { get; }

		internal float UpperHeight { get; }

		internal float CapEndForward { get; }

		internal float InclineLength => (float)Math.Sqrt((UpperForward - LowerForward) * (UpperForward - LowerForward) + (UpperHeight - LowerHeight) * (UpperHeight - LowerHeight));

		internal LadderLanding(float lowerForward, float lowerHeight, float upperForward, float upperHeight, float capEndForward)
		{
			LowerForward = lowerForward;
			LowerHeight = lowerHeight;
			UpperForward = upperForward;
			UpperHeight = upperHeight;
			CapEndForward = capEndForward;
		}
	}
	internal static class PrefabRules
	{
		private static readonly HashSet<string> Ladders = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "piece_wood_ladder", "piece_wood_ladder_45", "piece_woodstepladder", "piece_grausten_stoneladder", "wood_ladder", "wood_stepladder", "woodstepladder", "grausten_ladder", "grausten_stone_ladder", "piece_grausten_stone_ladder" };

		internal static bool IsVanillaLadder(string prefabName)
		{
			return Ladders.Contains(Clean(prefabName));
		}

		private static string Clean(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return string.Empty;
			}
			int num = value.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase);
			return ((num >= 0) ? value.Substring(0, num) : value).Trim();
		}
	}
	internal static class LegacyLadderConfigRules
	{
		internal const string CurrentFileName = "jg224.TrulySmoothLadders.cfg";

		internal const string GeneralTweaksFileName = "jg224.GeneralTweaks.cfg";

		internal const string LegacyGeneralTweaksFileName = "JG224.GeneralTweaks.cfg";

		internal static string FindImportSource(IEnumerable<string> candidatePaths)
		{
			string[] paths = ((candidatePaths == null) ? Array.Empty<string>() : candidatePaths.ToArray());
			if (FindExact(paths, "jg224.TrulySmoothLadders.cfg") != null)
			{
				return null;
			}
			return FindExact(paths, "jg224.GeneralTweaks.cfg") ?? FindExact(paths, "JG224.GeneralTweaks.cfg");
		}

		private static string FindExact(IEnumerable<string> paths, string fileName)
		{
			return paths.FirstOrDefault((string path) => string.Equals(Path.GetFileName(path), fileName, StringComparison.Ordinal));
		}
	}
}