Decompiled source of SkillLimitExtenderFork v1.0.0

plugins/SkillLimitExtender/skill_Limit_Extender.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("skill_Limit_Extender")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+cea2b9e20772a06bbb13306900b8227aabb27c88")]
[assembly: AssemblyProduct("skill_Limit_Extender")]
[assembly: AssemblyTitle("skill_Limit_Extender")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
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;
		}
	}
	[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 SkillLimitExtender
{
	internal static class SLE_ExtendedScaling
	{
		private static bool _hasServerOverride;

		private static bool _serverEnableDamageScaling;

		private static int _serverDamageReferenceSkillLevel;

		private static float _serverDamageReferenceMultiplier;

		private static float _serverVanillaDamageMultiplier;

		private static float _serverVanillaDamageExponent;

		private static bool _serverEnableExtendedCostScaling;

		private static float _serverExtendedCostExponent;

		private static bool _serverEnableBlockingScaling;

		private static bool _serverScaleSafeFallDistance;

		private static float _serverSafeFallDistanceExponent;

		internal static ConfigEntry<bool> EnableDamageScaling { get; private set; }

		internal static ConfigEntry<int> DamageReferenceSkillLevel { get; private set; }

		internal static ConfigEntry<float> DamageReferenceMultiplier { get; private set; }

		internal static ConfigEntry<float> VanillaDamageMultiplier { get; private set; }

		internal static ConfigEntry<float> VanillaDamageExponent { get; private set; }

		internal static ConfigEntry<bool> EnableExtendedCostScaling { get; private set; }

		internal static ConfigEntry<float> ExtendedCostExponent { get; private set; }

		internal static ConfigEntry<bool> EnableBlockingScaling { get; private set; }

		internal static ConfigEntry<bool> ScaleSafeFallDistance { get; private set; }

		internal static ConfigEntry<float> SafeFallDistanceExponent { get; private set; }

		internal static bool DamageScalingEnabled => _hasServerOverride ? _serverEnableDamageScaling : EnableDamageScaling.Value;

		internal static bool CostScalingEnabled => _hasServerOverride ? _serverEnableExtendedCostScaling : EnableExtendedCostScaling.Value;

		internal static bool BlockingScalingEnabled => _hasServerOverride ? _serverEnableBlockingScaling : EnableBlockingScaling.Value;

		internal static bool SafeFallDistanceScalingEnabled => _hasServerOverride ? _serverScaleSafeFallDistance : ScaleSafeFallDistance.Value;

		private static int EffectiveDamageReferenceSkillLevel => _hasServerOverride ? _serverDamageReferenceSkillLevel : DamageReferenceSkillLevel.Value;

		private static float EffectiveDamageReferenceMultiplier => _hasServerOverride ? _serverDamageReferenceMultiplier : DamageReferenceMultiplier.Value;

		private static float EffectiveVanillaDamageMultiplier => _hasServerOverride ? _serverVanillaDamageMultiplier : VanillaDamageMultiplier.Value;

		private static float EffectiveVanillaDamageExponent => _hasServerOverride ? _serverVanillaDamageExponent : VanillaDamageExponent.Value;

		private static float EffectiveExtendedCostExponent => _hasServerOverride ? _serverExtendedCostExponent : ExtendedCostExponent.Value;

		private static float EffectiveSafeFallDistanceExponent => _hasServerOverride ? _serverSafeFallDistanceExponent : SafeFallDistanceExponent.Value;

		internal static void Initialize(ConfigFile config)
		{
			EnableDamageScaling = config.Bind<bool>("3 - Extended Scaling", "EnableDamageScaling", true, "Enable SLE's damage scaling. From level 0-100 the configurable vanilla curve is used; above 100 the configured level-100 damage becomes the baseline for the extended curve. Blocking has a separate EnableBlockingScaling toggle but can reuse the same reference curve.");
			DamageReferenceSkillLevel = config.Bind<int>("3 - Extended Scaling", "DamageReferenceSkillLevel", 200, "Reference level used to calibrate the shared final multiplier above level 100. Must be above 100. Final formula: finalMultiplier = skillFactor^e, where e = ln(DamageReferenceMultiplier) / ln(DamageReferenceSkillLevel / 100). With Relative=false, skillFactor is level/100 until BonusCap is reached.");
			DamageReferenceMultiplier = config.Bind<float>("3 - Extended Scaling", "DamageReferenceMultiplier", 1.5f, "Final multiplier reached at DamageReferenceSkillLevel. For damage, it multiplies the configured level-100 damage. For Blocking, it multiplies only the vanilla level-100 skill bonus above the level-0 block value. Final formula: finalMultiplier = skillFactor^e, where e = ln(DamageReferenceMultiplier) / ln(DamageReferenceSkillLevel / 100). The per-skill BonusCap can stop further scaling by clamping skillFactor.");
			VanillaDamageMultiplier = config.Bind<float>("3 - Extended Scaling", "VanillaDamageMultiplier", 1f, "Controls how much of the normal skill-based damage increase is applied from level 0 to 100. 1.0 preserves vanilla; 0.5 keeps half of the normal skill contribution without halving base weapon damage. Final formula: center = 0.4 + 0.6 * (level / 100)^VanillaDamageExponent * VanillaDamageMultiplier.");
			VanillaDamageExponent = config.Bind<float>("3 - Extended Scaling", "VanillaDamageExponent", 1f, "Controls the shape of damage progression from level 0 to 100. 1.0 preserves vanilla; above 1.0 is slower early; below 1.0 is faster early. Final formula: center = 0.4 + 0.6 * (level / 100)^VanillaDamageExponent * VanillaDamageMultiplier.");
			EnableExtendedCostScaling = config.Bind<bool>("3 - Extended Scaling", "EnableExtendedCostScaling", true, "Extend decreasing skill-based values beyond level 100 without ever making them negative. This covers resource costs (stamina/eitr/health), decreasing action times such as bow draw/crossbow reload, and supported tool durability costs. Level 0-100 remains vanilla.");
			ExtendedCostExponent = config.Bind<float>("3 - Extended Scaling", "ExtendedCostExponent", 0.42f, "Controls how quickly costs/times continue decreasing above level 100. Higher values reduce them faster. The level-100 value is always preserved exactly and zero is approached asymptotically. Final formula for skillFactor > 1: value = valueAt100 / skillFactor^ExtendedCostExponent. With Relative=false, skillFactor = level/100 until BonusCap is reached. Example for a vanilla 33% discount: exponent 0.42 gives about 50% discount at level 200 and 58% at 300.");
			EnableBlockingScaling = config.Bind<bool>("3 - Extended Scaling", "EnableBlockingScaling", true, "If true, Blocking above level 100 extends only the skill-derived block bonus using the same final multiplier as damage. The level-0 block value is never multiplied. At factor 1 the vanilla level-100 value is preserved exactly. Final formula for factor > 1: finalBlockPower = blockAt0 + (blockAt100 - blockAt0) * skillFactor^e, where e = ln(DamageReferenceMultiplier) / ln(DamageReferenceSkillLevel / 100).");
			ScaleSafeFallDistance = config.Bind<bool>("3 - Extended Scaling", "ScaleSafeFallDistance", true, "If true, Jump skill above level 100 increases the no-damage fall-distance breakpoint. This changes only the safe fall breakpoint; it does not change jump force. Vanilla 4m is preserved at factor 1 and only the extra distance is extended. Final formula in meters for factor > 1: safeFallDistance = 4 + (skillFactor^SafeFallDistanceExponent - 1).");
			SafeFallDistanceExponent = config.Bind<float>("3 - Extended Scaling", "SafeFallDistanceExponent", 2f, "Controls how quickly the additional safe fall distance grows above Jump level 100. 2.0 gives 4m at factor 1, 7m at factor 2, 12m at factor 3, and 28m at factor 5. Final formula in meters for factor > 1: safeFallDistance = 4 + (skillFactor^SafeFallDistanceExponent - 1).");
		}

		internal static void GetDamageRange(float skillFactor, out float min, out float max)
		{
			if (!DamageScalingEnabled)
			{
				float num = Mathf.Lerp(0.4f, 1f, skillFactor);
				min = Mathf.Clamp01(num - 0.15f);
				max = Mathf.Clamp01(num + 0.15f);
			}
			else if (skillFactor <= 1f)
			{
				GetBaseDamageRange(skillFactor, out min, out max);
			}
			else
			{
				GetBaseDamageRange(1f, out var min2, out var max2);
				float extendedDamageMultiplier = GetExtendedDamageMultiplier(skillFactor);
				min = min2 * extendedDamageMultiplier;
				max = max2 * extendedDamageMultiplier;
			}
		}

		private static void GetBaseDamageRange(float skillFactor, out float min, out float max)
		{
			float baseDamageCenter = GetBaseDamageCenter(skillFactor);
			min = Mathf.Clamp01(baseDamageCenter - 0.15f);
			max = Mathf.Clamp01(baseDamageCenter + 0.15f);
		}

		private static float GetBaseDamageCenter(float skillFactor)
		{
			float num = Mathf.Clamp01(skillFactor);
			float num2 = Mathf.Max(0f, EffectiveVanillaDamageMultiplier);
			float num3 = SanitizePositiveExponent(EffectiveVanillaDamageExponent, 1f);
			float num4 = Mathf.Pow(num, num3);
			return 0.4f + 0.6f * num4 * num2;
		}

		internal static float GetExtendedDamageMultiplier(float skillFactor)
		{
			return DamageScalingEnabled ? GetExtendedFinalMultiplier(skillFactor) : 1f;
		}

		internal static float GetExtendedFinalMultiplier(float skillFactor)
		{
			if (skillFactor <= 1f)
			{
				return 1f;
			}
			float num = (float)EffectiveDamageReferenceSkillLevel / 100f;
			float num2 = EffectiveDamageReferenceMultiplier;
			if (num <= 1f || float.IsNaN(num) || float.IsInfinity(num))
			{
				num = 2f;
			}
			if (num2 <= 0f || float.IsNaN(num2) || float.IsInfinity(num2))
			{
				num2 = 1f;
			}
			if (Mathf.Approximately(num2, 1f))
			{
				return 1f;
			}
			float num3 = Mathf.Log(num2) / Mathf.Log(num);
			return Mathf.Pow(skillFactor, num3);
		}

		internal static float GetExtendedCostMultiplier(float skillFactor)
		{
			if (!CostScalingEnabled || skillFactor <= 1f)
			{
				return 1f;
			}
			float num = SanitizePositiveExponent(EffectiveExtendedCostExponent, 0.42f);
			return 1f / Mathf.Pow(skillFactor, num);
		}

		internal static float ExtendedCostLerp(float from, float at100, float skillFactor)
		{
			if (!CostScalingEnabled || skillFactor <= 1f)
			{
				return Mathf.Lerp(from, at100, skillFactor);
			}
			if (at100 >= from)
			{
				return at100;
			}
			if (at100 < 0f)
			{
				return at100;
			}
			return at100 * GetExtendedCostMultiplier(skillFactor);
		}

		internal static float GetSafeFallDistance(float jumpSkillFactor)
		{
			if (jumpSkillFactor <= 1f)
			{
				return 4f;
			}
			float num = SanitizePositiveExponent(EffectiveSafeFallDistanceExponent, 2f);
			return 4f + (Mathf.Pow(jumpSkillFactor, num) - 1f);
		}

		internal static string BuildSyncRecord()
		{
			return string.Join("|", "E", EnableDamageScaling.Value ? "1" : "0", DamageReferenceSkillLevel.Value.ToString(CultureInfo.InvariantCulture), DamageReferenceMultiplier.Value.ToString("R", CultureInfo.InvariantCulture), VanillaDamageMultiplier.Value.ToString("R", CultureInfo.InvariantCulture), VanillaDamageExponent.Value.ToString("R", CultureInfo.InvariantCulture), EnableExtendedCostScaling.Value ? "1" : "0", ExtendedCostExponent.Value.ToString("R", CultureInfo.InvariantCulture), EnableBlockingScaling.Value ? "1" : "0", ScaleSafeFallDistance.Value ? "1" : "0", SafeFallDistanceExponent.Value.ToString("R", CultureInfo.InvariantCulture));
		}

		internal static void ApplyServerRecord(string record)
		{
			if (string.IsNullOrWhiteSpace(record))
			{
				_hasServerOverride = false;
				return;
			}
			string[] array = record.Split(new char[1] { '|' });
			if (array.Length == 11 && !(array[0] != "E"))
			{
				_serverEnableDamageScaling = array[1] == "1";
				_serverDamageReferenceSkillLevel = ParseInt(array[2], 200);
				_serverDamageReferenceMultiplier = ParseFloat(array[3], 1.5f);
				_serverVanillaDamageMultiplier = ParseFloat(array[4], 1f);
				_serverVanillaDamageExponent = ParseFloat(array[5], 1f);
				_serverEnableExtendedCostScaling = array[6] == "1";
				_serverExtendedCostExponent = ParseFloat(array[7], 0.42f);
				_serverEnableBlockingScaling = array[8] == "1";
				_serverScaleSafeFallDistance = array[9] == "1";
				_serverSafeFallDistanceExponent = ParseFloat(array[10], 2f);
				_hasServerOverride = true;
			}
		}

		private static float SanitizePositiveExponent(float value, float fallback)
		{
			return (value > 0f && !float.IsNaN(value) && !float.IsInfinity(value)) ? value : fallback;
		}

		private static int ParseInt(string value, int fallback)
		{
			int result;
			return int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result) ? result : fallback;
		}

		private static float ParseFloat(string value, float fallback)
		{
			float result;
			return float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out result) ? result : fallback;
		}
	}
	[HarmonyPatch(typeof(Skills), "CheatRaiseSkill")]
	[HarmonyPatch(new Type[]
	{
		typeof(string),
		typeof(float),
		typeof(bool)
	})]
	internal static class SLE_CheatRaiseSkill_Transpiler
	{
		[HarmonyTranspiler]
		[HarmonyPriority(-9000)]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Expected O, but got Unknown
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Expected O, but got Unknown
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			MethodInfo methodInfo = AccessTools.Method(typeof(SkillConfigManager), "GetCap", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(SkillConfigManager), "GetCapByName", (Type[])null, (Type[])null);
			FieldInfo fieldInfo = AccessTools.Field(typeof(Skill), "m_info");
			FieldInfo fieldInfo2 = AccessTools.Field(typeof(SkillDef), "m_skill");
			if (methodInfo == null || fieldInfo == null || fieldInfo2 == null)
			{
				return list;
			}
			for (int i = 0; i < list.Count; i++)
			{
				CodeInstruction val = list[i];
				if (!(val.opcode == OpCodes.Ldc_R4) || !(val.operand is float num) || !(Math.Abs(num - 100f) < 0.0001f))
				{
					continue;
				}
				bool flag = false;
				for (int j = Math.Max(0, i - 5); j < Math.Min(list.Count, i + 5); j++)
				{
					if (list[j].opcode == OpCodes.Call)
					{
						object operand = list[j].operand;
						if (operand != null && operand.ToString()?.Contains("Clamp") == true)
						{
							flag = true;
							break;
						}
					}
				}
				if (!flag || !(methodInfo2 != null))
				{
					continue;
				}
				List<CodeInstruction> list2 = new List<CodeInstruction>
				{
					new CodeInstruction(OpCodes.Ldarg_1, (object)null),
					new CodeInstruction(OpCodes.Call, (object)methodInfo2),
					new CodeInstruction(OpCodes.Conv_R4, (object)null)
				};
				list[i] = list2[0];
				list.InsertRange(i + 1, list2.GetRange(1, list2.Count - 1));
				ConfigEntry<bool> enableGrowthCurveDebug = SkillLimitExtenderPlugin.EnableGrowthCurveDebug;
				if (enableGrowthCurveDebug != null && enableGrowthCurveDebug.Value)
				{
					ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
					if (logger != null)
					{
						logger.LogDebug((object)"[SLE] CheatRaiseSkill: Replaced 100f with GetCapByName(name)");
					}
				}
				break;
			}
			return list;
		}
	}
	internal static class SLE_ExtendedScalingPatches
	{
		internal sealed class CostState
		{
			internal bool Active;

			internal float SkillFactor;

			internal IDisposable? OverrideScope;

			internal void Restore()
			{
				OverrideScope?.Dispose();
				OverrideScope = null;
			}
		}

		private static readonly MethodInfo? VanillaLerp = AccessTools.Method(typeof(Mathf), "Lerp", new Type[3]
		{
			typeof(float),
			typeof(float),
			typeof(float)
		}, (Type[])null);

		private static readonly MethodInfo? ExtendedLerp = AccessTools.Method(typeof(SLE_ExtendedScaling), "ExtendedCostLerp", (Type[])null, (Type[])null);

		internal static IEnumerable<CodeInstruction> ReplaceLerps(IEnumerable<CodeInstruction> instructions, string methodName, int expectedCount)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			if (VanillaLerp == null || ExtendedLerp == null)
			{
				ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
				if (logger != null)
				{
					logger.LogError((object)("[SLE] " + methodName + ": could not resolve Lerp helper methods."));
				}
				return list;
			}
			int num = 0;
			foreach (CodeInstruction item in list)
			{
				if (item.opcode == OpCodes.Call && object.Equals(item.operand, VanillaLerp))
				{
					item.operand = ExtendedLerp;
					num++;
				}
			}
			if (num != expectedCount)
			{
				ManualLogSource logger2 = SkillLimitExtenderPlugin.Logger;
				if (logger2 != null)
				{
					logger2.LogWarning((object)($"[SLE] {methodName}: expected {expectedCount} Mathf.Lerp call(s), found {num}. " + "Only the calls that were found were replaced."));
				}
			}
			else
			{
				ManualLogSource logger3 = SkillLimitExtenderPlugin.Logger;
				if (logger3 != null)
				{
					logger3.LogInfo((object)$"[SLE] {methodName}: extended {num} decreasing skill Lerp(s) above level 100.");
				}
			}
			return list;
		}

		internal static CostState BeginCostOverride(Player? player, SkillType skillType)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Invalid comparison between Unknown and I4
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			CostState costState = new CostState();
			if (!SLE_ExtendedScaling.CostScalingEnabled || (Object)(object)player == (Object)null || (int)skillType == 0)
			{
				return costState;
			}
			float skillFactor = ((Character)player).GetSkillFactor(skillType);
			if (skillFactor <= 1f)
			{
				return costState;
			}
			costState.Active = true;
			costState.SkillFactor = skillFactor;
			costState.OverrideScope = SLE_SkillFactorOverride.Push(skillType, 1f);
			return costState;
		}

		internal static CostState BeginAttackCostOverride(Character? character, ItemData? item = null)
		{
			//IL_0063: 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_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			Player val = (Player)(object)((character is Player) ? character : null);
			if (val == null)
			{
				return new CostState();
			}
			ItemData val2 = item;
			if (val2 == null)
			{
				Humanoid val3 = (Humanoid)(object)((character is Humanoid) ? character : null);
				if (val3 != null)
				{
					val2 = val3.GetCurrentWeapon();
				}
			}
			SkillType valueOrDefault = (val2?.m_shared?.m_skillType).GetValueOrDefault();
			return BeginCostOverride(val, valueOrDefault);
		}

		internal static SkillType GetBuildSkill(Player player, ItemData? item = null)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			//IL_00a5: 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_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Invalid comparison between Unknown and I4
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			if (item?.m_shared != null && (int)item.m_shared.m_skillType > 0)
			{
				return item.m_shared.m_skillType;
			}
			try
			{
				PieceTable value = Traverse.Create((object)player).Field("m_buildPieces").GetValue<PieceTable>();
				if ((Object)(object)value != (Object)null && (int)value.m_skill > 0)
				{
					return value.m_skill;
				}
			}
			catch
			{
			}
			return (((Humanoid)player).GetCurrentWeapon()?.m_shared?.m_skillType).GetValueOrDefault();
		}

		internal static void FinishCost(ref float result, CostState? state)
		{
			if (state != null)
			{
				state.Restore();
				if (state.Active)
				{
					float num = Mathf.Max(0f, result);
					result = num * SLE_ExtendedScaling.GetExtendedCostMultiplier(state.SkillFactor);
				}
			}
		}

		internal static Exception? RestoreOnException(Exception? exception, CostState? state)
		{
			state?.Restore();
			return exception;
		}
	}
	[HarmonyPatch(typeof(Player), "CheckRun", new Type[]
	{
		typeof(Vector3),
		typeof(float)
	})]
	internal static class SLE_Player_CheckRun_Extended_Patch
	{
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return SLE_ExtendedScalingPatches.ReplaceLerps(instructions, "Player.CheckRun", 1);
		}
	}
	[HarmonyPatch(typeof(Player), "OnSwimming", new Type[]
	{
		typeof(Vector3),
		typeof(float)
	})]
	internal static class SLE_Player_OnSwimming_Extended_Patch
	{
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return SLE_ExtendedScalingPatches.ReplaceLerps(instructions, "Player.OnSwimming", 1);
		}
	}
	[HarmonyPatch(typeof(Player), "GetDodgeStaminaUse")]
	internal static class SLE_Player_GetDodgeStaminaUse_Extended_Patch
	{
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return SLE_ExtendedScalingPatches.ReplaceLerps(instructions, "Player.GetDodgeStaminaUse", 1);
		}
	}
	[HarmonyPatch(typeof(Player), "OnSneaking", new Type[] { typeof(float) })]
	internal static class SLE_Player_OnSneaking_Extended_Patch
	{
		private sealed class State
		{
			internal bool Active;

			internal float OriginalDrain;
		}

		[HarmonyPrefix]
		private static void Prefix(Player __instance, out State __state)
		{
			__state = new State();
			if (SLE_ExtendedScaling.CostScalingEnabled && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				float skillFactor = ((Character)__instance).GetSkillFactor((SkillType)101);
				if (!(skillFactor <= 1f))
				{
					__state.Active = true;
					__state.OriginalDrain = __instance.m_sneakStaminaDrain;
					__instance.m_sneakStaminaDrain = __state.OriginalDrain * SLE_ExtendedScaling.GetExtendedCostMultiplier(skillFactor);
				}
			}
		}

		[HarmonyPostfix]
		private static void Postfix(Player __instance, State __state)
		{
			if (__state != null && __state.Active)
			{
				__instance.m_sneakStaminaDrain = __state.OriginalDrain;
			}
		}

		[HarmonyFinalizer]
		private static Exception? Finalizer(Exception? __exception, Player __instance, State __state)
		{
			if (__state != null && __state.Active)
			{
				__instance.m_sneakStaminaDrain = __state.OriginalDrain;
			}
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Humanoid), "GetAttackDrawPercentage")]
	internal static class SLE_Humanoid_GetAttackDrawPercentage_Extended_Patch
	{
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return SLE_ExtendedScalingPatches.ReplaceLerps(instructions, "Humanoid.GetAttackDrawPercentage", 1);
		}
	}
	[HarmonyPatch(typeof(ItemData), "GetWeaponLoadingTime")]
	internal static class SLE_ItemData_GetWeaponLoadingTime_Extended_Patch
	{
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return SLE_ExtendedScalingPatches.ReplaceLerps(instructions, "ItemData.GetWeaponLoadingTime", 1);
		}
	}
	[HarmonyPatch(typeof(Sadle), "UpdateRiding", new Type[] { typeof(float) })]
	internal static class SLE_Sadle_UpdateRiding_Extended_Patch
	{
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return SLE_ExtendedScalingPatches.ReplaceLerps(instructions, "Sadle.UpdateRiding", 1);
		}
	}
	[HarmonyPatch(typeof(FishingFloat), "FixedUpdate")]
	internal static class SLE_FishingFloat_FixedUpdate_Extended_Patch
	{
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return SLE_ExtendedScalingPatches.ReplaceLerps(instructions, "FishingFloat.FixedUpdate", 3);
		}
	}
	[HarmonyPatch(typeof(Attack), "GetAttackStamina", new Type[] { })]
	internal static class SLE_Attack_GetAttackStamina_Extended_Patch
	{
		[HarmonyPrefix]
		private static void Prefix(Humanoid ___m_character, out SLE_ExtendedScalingPatches.CostState __state)
		{
			__state = SLE_ExtendedScalingPatches.BeginAttackCostOverride((Character?)(object)___m_character);
		}

		[HarmonyPostfix]
		private static void Postfix(ref float __result, SLE_ExtendedScalingPatches.CostState __state)
		{
			SLE_ExtendedScalingPatches.FinishCost(ref __result, __state);
		}

		[HarmonyFinalizer]
		private static Exception? Finalizer(Exception? __exception, SLE_ExtendedScalingPatches.CostState __state)
		{
			return SLE_ExtendedScalingPatches.RestoreOnException(__exception, __state);
		}
	}
	[HarmonyPatch(typeof(Attack), "GetAttackEitr", new Type[]
	{
		typeof(Character),
		typeof(ItemData)
	})]
	internal static class SLE_Attack_GetAttackEitr_Extended_Patch
	{
		[HarmonyPrefix]
		private static void Prefix(Character __0, ItemData __1, out SLE_ExtendedScalingPatches.CostState __state)
		{
			__state = SLE_ExtendedScalingPatches.BeginAttackCostOverride(__0, __1);
		}

		[HarmonyPostfix]
		private static void Postfix(ref float __result, SLE_ExtendedScalingPatches.CostState __state)
		{
			SLE_ExtendedScalingPatches.FinishCost(ref __result, __state);
		}

		[HarmonyFinalizer]
		private static Exception? Finalizer(Exception? __exception, SLE_ExtendedScalingPatches.CostState __state)
		{
			return SLE_ExtendedScalingPatches.RestoreOnException(__exception, __state);
		}
	}
	[HarmonyPatch(typeof(Attack), "GetAttackHealth", new Type[] { })]
	internal static class SLE_Attack_GetAttackHealth_Extended_Patch
	{
		[HarmonyPrefix]
		private static void Prefix(Humanoid ___m_character, out SLE_ExtendedScalingPatches.CostState __state)
		{
			__state = SLE_ExtendedScalingPatches.BeginAttackCostOverride((Character?)(object)___m_character);
		}

		[HarmonyPostfix]
		private static void Postfix(ref float __result, SLE_ExtendedScalingPatches.CostState __state)
		{
			SLE_ExtendedScalingPatches.FinishCost(ref __result, __state);
		}

		[HarmonyFinalizer]
		private static Exception? Finalizer(Exception? __exception, SLE_ExtendedScalingPatches.CostState __state)
		{
			return SLE_ExtendedScalingPatches.RestoreOnException(__exception, __state);
		}
	}
	[HarmonyPatch(typeof(ItemData), "GetDrawStaminaDrain", new Type[] { })]
	internal static class SLE_ItemData_GetDrawStaminaDrain_Extended_Patch
	{
		[HarmonyPrefix]
		private static void Prefix(ItemData __instance, out SLE_ExtendedScalingPatches.CostState __state)
		{
			//IL_0024: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			SkillType valueOrDefault = (__instance?.m_shared?.m_skillType).GetValueOrDefault();
			__state = SLE_ExtendedScalingPatches.BeginCostOverride(Player.m_localPlayer, valueOrDefault);
		}

		[HarmonyPostfix]
		private static void Postfix(ref float __result, SLE_ExtendedScalingPatches.CostState __state)
		{
			SLE_ExtendedScalingPatches.FinishCost(ref __result, __state);
		}

		[HarmonyFinalizer]
		private static Exception? Finalizer(Exception? __exception, SLE_ExtendedScalingPatches.CostState __state)
		{
			return SLE_ExtendedScalingPatches.RestoreOnException(__exception, __state);
		}
	}
	[HarmonyPatch(typeof(ItemData), "GetDrawEitrDrain", new Type[] { })]
	internal static class SLE_ItemData_GetDrawEitrDrain_Extended_Patch
	{
		[HarmonyPrefix]
		private static void Prefix(ItemData __instance, out SLE_ExtendedScalingPatches.CostState __state)
		{
			//IL_0024: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			SkillType valueOrDefault = (__instance?.m_shared?.m_skillType).GetValueOrDefault();
			__state = SLE_ExtendedScalingPatches.BeginCostOverride(Player.m_localPlayer, valueOrDefault);
		}

		[HarmonyPostfix]
		private static void Postfix(ref float __result, SLE_ExtendedScalingPatches.CostState __state)
		{
			SLE_ExtendedScalingPatches.FinishCost(ref __result, __state);
		}

		[HarmonyFinalizer]
		private static Exception? Finalizer(Exception? __exception, SLE_ExtendedScalingPatches.CostState __state)
		{
			return SLE_ExtendedScalingPatches.RestoreOnException(__exception, __state);
		}
	}
	[HarmonyPatch(typeof(Player), "GetBuildStamina", new Type[] { })]
	internal static class SLE_Player_GetBuildStamina_Extended_Patch
	{
		[HarmonyPrefix]
		private static void Prefix(Player __instance, out SLE_ExtendedScalingPatches.CostState __state)
		{
			//IL_0003: 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_000b: Unknown result type (might be due to invalid IL or missing references)
			SkillType buildSkill = SLE_ExtendedScalingPatches.GetBuildSkill(__instance);
			__state = SLE_ExtendedScalingPatches.BeginCostOverride(__instance, buildSkill);
		}

		[HarmonyPostfix]
		private static void Postfix(ref float __result, SLE_ExtendedScalingPatches.CostState __state)
		{
			SLE_ExtendedScalingPatches.FinishCost(ref __result, __state);
		}

		[HarmonyFinalizer]
		private static Exception? Finalizer(Exception? __exception, SLE_ExtendedScalingPatches.CostState __state)
		{
			return SLE_ExtendedScalingPatches.RestoreOnException(__exception, __state);
		}
	}
	[HarmonyPatch(typeof(Player), "GetPlaceDurability", new Type[] { typeof(ItemData) })]
	internal static class SLE_Player_GetPlaceDurability_Extended_Patch
	{
		[HarmonyPrefix]
		private static void Prefix(Player __instance, ItemData __0, out SLE_ExtendedScalingPatches.CostState __state)
		{
			//IL_0003: 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_000b: Unknown result type (might be due to invalid IL or missing references)
			SkillType buildSkill = SLE_ExtendedScalingPatches.GetBuildSkill(__instance, __0);
			__state = SLE_ExtendedScalingPatches.BeginCostOverride(__instance, buildSkill);
		}

		[HarmonyPostfix]
		private static void Postfix(ref float __result, SLE_ExtendedScalingPatches.CostState __state)
		{
			SLE_ExtendedScalingPatches.FinishCost(ref __result, __state);
		}

		[HarmonyFinalizer]
		private static Exception? Finalizer(Exception? __exception, SLE_ExtendedScalingPatches.CostState __state)
		{
			return SLE_ExtendedScalingPatches.RestoreOnException(__exception, __state);
		}
	}
	[HarmonyPatch(typeof(ItemData), "GetBlockPower", new Type[]
	{
		typeof(int),
		typeof(float)
	})]
	internal static class SLE_ItemData_GetBlockPower_Extended_Patch
	{
		[HarmonyPostfix]
		private static void Postfix(ItemData __instance, ref float __result, int __0, float __1)
		{
			if (SLE_ExtendedScaling.BlockingScalingEnabled && !(__1 <= 1f))
			{
				float baseBlockPower = __instance.GetBaseBlockPower(__0);
				float num = baseBlockPower * 0.5f;
				float extendedFinalMultiplier = SLE_ExtendedScaling.GetExtendedFinalMultiplier(__1);
				__result = baseBlockPower + num * extendedFinalMultiplier;
			}
		}
	}
	[HarmonyPatch(typeof(Character), "UpdateGroundContact")]
	internal static class SLE_Character_UpdateGroundContact_JumpFall_Patch
	{
		[HarmonyPrefix]
		private static void Prefix(Character __instance, ref float ___m_maxAirAltitude, bool ___m_groundContact)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			if (!SLE_ExtendedScaling.SafeFallDistanceScalingEnabled || !___m_groundContact || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			float skillFactor = ((Character)val).GetSkillFactor((SkillType)100);
			if (!(skillFactor <= 1f))
			{
				float y = ((Component)__instance).transform.position.y;
				float num = ___m_maxAirAltitude - y;
				if (!(num <= 4f))
				{
					float safeFallDistance = SLE_ExtendedScaling.GetSafeFallDistance(skillFactor);
					float num2 = Mathf.Max(0f, safeFallDistance - 4f);
					float num3 = Mathf.Max(0f, num - num2);
					___m_maxAirAltitude = y + num3;
				}
			}
		}
	}
	[HarmonyPatch(typeof(SkillsDialog), "Setup")]
	internal static class SLE_Hook_SkillsDialog_LevelBars
	{
		[HarmonyPrefix]
		private static bool Prefix(ref Player player)
		{
			try
			{
				if ((Object)(object)player == (Object)null)
				{
					player = SLE_SkillHelpers.GetSafeLocalPlayer();
				}
				if ((Object)(object)player == (Object)null)
				{
					ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
					if (logger != null)
					{
						logger.LogWarning((object)"[SLE] SkillsDialog.Setup called without a valid player; skipping dialog setup");
					}
					return false;
				}
				Skills skills = ((Character)player).GetSkills();
				if ((Object)(object)skills == (Object)null)
				{
					ManualLogSource logger2 = SkillLimitExtenderPlugin.Logger;
					if (logger2 != null)
					{
						logger2.LogWarning((object)"[SLE] SkillsDialog.Setup called while Player.GetSkills() is null; skipping dialog setup");
					}
					return false;
				}
				SLE_SkillDataSanitizer.Sanitize(skills, "SkillsDialog", logUnresolved: true);
				ConfigEntry<bool> enableGrowthCurveDebug = SkillLimitExtenderPlugin.EnableGrowthCurveDebug;
				if (enableGrowthCurveDebug != null && enableGrowthCurveDebug.Value)
				{
					ManualLogSource logger3 = SkillLimitExtenderPlugin.Logger;
					if (logger3 != null)
					{
						logger3.LogDebug((object)("[SLE] SkillsDialog.Setup - Player: " + player.GetPlayerName()));
					}
				}
				return true;
			}
			catch (Exception arg)
			{
				ManualLogSource logger4 = SkillLimitExtenderPlugin.Logger;
				if (logger4 != null)
				{
					logger4.LogError((object)$"[SLE] SkillsDialog.Setup preflight failed: {arg}");
				}
				return false;
			}
		}

		[HarmonyTranspiler]
		[HarmonyPriority(-9000)]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			MethodInfo methodInfo = AccessTools.Method(typeof(SkillConfigManager), "GetUiDenominator", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)"[SLE] SkillsDialog: GetUiDenominator method missing; keeping vanilla UI denominator");
				}
				return list;
			}
			int num = 0;
			for (int i = 0; i < list.Count - 1; i++)
			{
				CodeInstruction val = list[i];
				CodeInstruction val2 = list[i + 1];
				if (val.opcode != OpCodes.Ldc_R4 || !(val.operand is float num2) || Math.Abs(num2 - 100f) >= 0.0001f || val2.opcode != OpCodes.Div)
				{
					continue;
				}
				bool flag = false;
				for (int j = i + 1; j < Math.Min(list.Count, i + 12); j++)
				{
					if (list[j].opcode == OpCodes.Call || list[j].opcode == OpCodes.Callvirt)
					{
						object operand = list[j].operand;
						if (operand != null && operand.ToString()?.Contains("SetValue") == true)
						{
							flag = true;
							break;
						}
					}
				}
				if (flag)
				{
					val.opcode = OpCodes.Call;
					val.operand = methodInfo;
					num++;
				}
			}
			ManualLogSource logger2 = SkillLimitExtenderPlugin.Logger;
			if (logger2 != null)
			{
				logger2.LogInfo((object)$"[SLE] SkillsDialog: replaced {num} UI denominator constant(s)");
			}
			return list;
		}

		[HarmonyFinalizer]
		private static Exception Finalizer(Exception __exception, Player player)
		{
			if (__exception == null)
			{
				return null;
			}
			try
			{
				ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
				if (logger != null)
				{
					logger.LogError((object)("[SLE] SkillsDialog.Setup threw " + __exception.GetType().Name + ": " + __exception.Message));
				}
				Skills val = ((player != null) ? ((Character)player).GetSkills() : null);
				if ((Object)(object)val != (Object)null)
				{
					SLE_SkillDataSanitizer.Sanitize(val, "SkillsDialog-finalizer", logUnresolved: true);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger2 = SkillLimitExtenderPlugin.Logger;
				if (logger2 != null)
				{
					logger2.LogWarning((object)("[SLE] SkillsDialog diagnostics failed: " + ex.Message));
				}
			}
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Skills), "GetSkillFactor")]
	internal static class SLE_Hook_Skills_GetSkillFactor
	{
		[HarmonyPrefix]
		private static bool Prefix(Skills __instance, SkillType skillType, ref float __result)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_0026: 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_005a: 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_00bb: 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)
			try
			{
				if ((int)skillType == 0 || (int)skillType == 999)
				{
					__result = 0f;
					return false;
				}
				if (SLE_SkillFactorOverride.TryGet(skillType, out var factor))
				{
					__result = factor;
					return false;
				}
				float num = __instance.GetSkillSafe(skillType)?.m_level ?? 0f;
				int num2 = Math.Max(1, SkillConfigManager.GetBonusCap(skillType));
				float num3 = (float)num2 / 100f;
				float num6;
				if (SkillConfigManager.IsRelative(skillType))
				{
					int cap = SkillConfigManager.GetCap(skillType);
					float num4 = ((cap > 0) ? ((float)cap) : 100f);
					float num5 = ((num4 > 0f) ? (num / num4) : 0f);
					num6 = num5 * num3;
				}
				else
				{
					float factorDenominator = SkillConfigManager.GetFactorDenominator(skillType);
					num6 = num / factorDenominator;
				}
				if (num6 < 0f)
				{
					num6 = 0f;
				}
				if (num6 > num3)
				{
					num6 = num3;
				}
				__result = num6;
				return false;
			}
			catch
			{
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(Skills), "Save")]
	internal static class SLE_Hook_Skills_Save_Cleanup
	{
		private readonly struct SavedSkill
		{
			internal readonly SkillType Type;

			internal readonly float Level;

			internal readonly float Accumulator;

			internal SavedSkill(SkillType type, float level, float accumulator)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0003: Unknown result type (might be due to invalid IL or missing references)
				Type = type;
				Level = level;
				Accumulator = accumulator;
			}
		}

		[HarmonyPrefix]
		private static bool Prefix(Skills __instance, ZPackage pkg)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Invalid comparison between Unknown and I4
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Expected I4, but got Unknown
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Expected I4, but got Unknown
			try
			{
				SLE_SkillDataSanitizer.Sanitize(__instance, "save", logUnresolved: true);
				Dictionary<SkillType, Skill> value = Traverse.Create((object)__instance).Field("m_skillData").GetValue<Dictionary<SkillType, Skill>>();
				if (value == null)
				{
					ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
					if (logger != null)
					{
						logger.LogError((object)"[SLE] Skills.Save: m_skillData is null; writing an empty valid skill block");
					}
					pkg.Write(2);
					pkg.Write(0);
					return false;
				}
				List<SavedSkill> list = new List<SavedSkill>(value.Count);
				int num = 0;
				foreach (KeyValuePair<SkillType, Skill> item in value)
				{
					if ((int)item.Key == 0)
					{
						continue;
					}
					Skill value2 = item.Value;
					if (value2 == null)
					{
						num++;
						ManualLogSource logger2 = SkillLimitExtenderPlugin.Logger;
						if (logger2 != null)
						{
							logger2.LogWarning((object)$"[SLE] Skills.Save: skipped null skill entry {item.Key} ({(int)item.Key})");
						}
					}
					else
					{
						list.Add(new SavedSkill(item.Key, value2.m_level, value2.m_accumulator));
					}
				}
				pkg.Write(2);
				pkg.Write(list.Count);
				for (int i = 0; i < list.Count; i++)
				{
					SavedSkill savedSkill = list[i];
					pkg.Write((int)savedSkill.Type);
					pkg.Write(savedSkill.Level);
					pkg.Write(savedSkill.Accumulator);
				}
				if (num > 0)
				{
					ManualLogSource logger3 = SkillLimitExtenderPlugin.Logger;
					if (logger3 != null)
					{
						logger3.LogWarning((object)string.Format("[SLE] Skills.Save: wrote {0} skills and skipped {1} null entr{2}", list.Count, num, (num == 1) ? "y" : "ies"));
					}
				}
				else
				{
					ConfigEntry<bool> enableGrowthCurveDebug = SkillLimitExtenderPlugin.EnableGrowthCurveDebug;
					if (enableGrowthCurveDebug != null && enableGrowthCurveDebug.Value)
					{
						ManualLogSource logger4 = SkillLimitExtenderPlugin.Logger;
						if (logger4 != null)
						{
							logger4.LogDebug((object)$"[SLE] Skills.Save: wrote {list.Count} skills");
						}
					}
				}
				return false;
			}
			catch (Exception arg)
			{
				ManualLogSource logger5 = SkillLimitExtenderPlugin.Logger;
				if (logger5 != null)
				{
					logger5.LogError((object)$"[SLE] Skills.Save replacement failed; falling back to vanilla Save: {arg}");
				}
				return true;
			}
		}
	}
	internal static class SLE_SkillDataSanitizer
	{
		internal readonly struct Result
		{
			internal readonly int Removed;

			internal readonly int Repaired;

			internal readonly int Unresolved;

			internal bool Changed => Removed > 0 || Repaired > 0;

			internal Result(int removed, int repaired, int unresolved)
			{
				Removed = removed;
				Repaired = repaired;
				Unresolved = unresolved;
			}
		}

		internal static Result Sanitize(Skills skills, string source, bool logUnresolved)
		{
			//IL_0096: 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_00a9: Invalid comparison between Unknown and I4
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Expected I4, but got Unknown
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)skills == (Object)null)
			{
				return default(Result);
			}
			Dictionary<SkillType, Skill> value = Traverse.Create((object)skills).Field("m_skillData").GetValue<Dictionary<SkillType, Skill>>();
			if (value == null || value.Count == 0)
			{
				return default(Result);
			}
			List<SkillDef> value2 = Traverse.Create((object)skills).Field("m_skills").GetValue<List<SkillDef>>();
			List<SkillType> list = new List<SkillType>();
			int num = 0;
			int num2 = 0;
			foreach (KeyValuePair<SkillType, Skill> item in value)
			{
				if ((int)item.Key == 0 || (int)item.Key == 999 || item.Value == null)
				{
					list.Add(item.Key);
					continue;
				}
				Skill value3 = item.Value;
				SkillDef value4 = Traverse.Create((object)value3).Field("m_info").GetValue<SkillDef>();
				if (value4 != null)
				{
					continue;
				}
				SkillDef val = null;
				if (value2 != null)
				{
					for (int i = 0; i < value2.Count; i++)
					{
						SkillDef val2 = value2[i];
						if (val2 != null && val2.m_skill == item.Key)
						{
							val = val2;
							break;
						}
					}
				}
				if (val != null)
				{
					Traverse.Create((object)value3).Field("m_info").SetValue((object)val);
					num++;
					continue;
				}
				num2++;
				if (logUnresolved)
				{
					ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
					if (logger != null)
					{
						logger.LogWarning((object)($"[SLE] Skill data ({source}): could not resolve SkillDef for {item.Key} ({(int)item.Key}), " + $"level={value3.m_level:F2}, accumulator={value3.m_accumulator:F2}"));
					}
				}
			}
			for (int j = 0; j < list.Count; j++)
			{
				value.Remove(list[j]);
			}
			if (list.Count > 0 || num > 0)
			{
				ManualLogSource logger2 = SkillLimitExtenderPlugin.Logger;
				if (logger2 != null)
				{
					logger2.LogWarning((object)(string.Format("[SLE] Skill data ({0}): removed {1} invalid entr{2}, ", source, list.Count, (list.Count == 1) ? "y" : "ies") + string.Format("repaired {0} SkillDef reference{1}", num, (num == 1) ? "" : "s")));
				}
			}
			return new Result(list.Count, num, num2);
		}
	}
	[HarmonyPatch(typeof(Skills), "Load", new Type[] { typeof(ZPackage) })]
	internal static class SLE_Hook_Skills_Load_Sanitize
	{
		[HarmonyPostfix]
		[HarmonyPriority(-9000)]
		private static void Postfix(Skills __instance)
		{
			try
			{
				SLE_SkillDataSanitizer.Sanitize(__instance, "load", logUnresolved: true);
			}
			catch (Exception arg)
			{
				ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
				if (logger != null)
				{
					logger.LogError((object)$"[SLE] Skill data sanitize after load failed: {arg}");
				}
			}
		}
	}
	internal static class SLE_SkillFactorOverride
	{
		private readonly struct Entry
		{
			internal readonly SkillType SkillType;

			internal readonly float Factor;

			internal Entry(SkillType skillType, float factor)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0003: Unknown result type (might be due to invalid IL or missing references)
				SkillType = skillType;
				Factor = factor;
			}
		}

		private sealed class Scope : IDisposable
		{
			private int _index;

			private bool _disposed;

			internal Scope(int index)
			{
				_index = index;
			}

			public void Dispose()
			{
				if (_disposed)
				{
					return;
				}
				_disposed = true;
				if (_entries != null && _entries.Count != 0)
				{
					if (_index == _entries.Count - 1)
					{
						_entries.RemoveAt(_index);
					}
					else if (_index >= 0 && _index < _entries.Count)
					{
						_entries.RemoveAt(_index);
					}
				}
			}
		}

		[ThreadStatic]
		private static List<Entry>? _entries;

		internal static IDisposable Push(SkillType skillType, float factor)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if (_entries == null)
			{
				_entries = new List<Entry>();
			}
			_entries.Add(new Entry(skillType, factor));
			return new Scope(_entries.Count - 1);
		}

		internal static bool TryGet(SkillType skillType, out float factor)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			if (_entries != null)
			{
				for (int num = _entries.Count - 1; num >= 0; num--)
				{
					if (_entries[num].SkillType == skillType)
					{
						factor = _entries[num].Factor;
						return true;
					}
				}
			}
			factor = 0f;
			return false;
		}
	}
	internal static class SLE_SkillHelpers
	{
		internal static Player? GetSafeLocalPlayer()
		{
			try
			{
				if ((Object)(object)Player.m_localPlayer != (Object)null)
				{
					return Player.m_localPlayer;
				}
				Game instance = Game.instance;
				if (instance == null)
				{
					goto IL_0066;
				}
				PlayerProfile playerProfile = instance.GetPlayerProfile();
				if (((playerProfile != null) ? new long?(playerProfile.GetPlayerID()) : ((long?)null)) != 0)
				{
					goto IL_0066;
				}
				goto end_IL_0001;
				IL_0066:
				List<Player> allPlayers = Player.GetAllPlayers();
				if (allPlayers != null)
				{
					foreach (Player item in allPlayers)
					{
						if ((Object)(object)item != (Object)null && ((Character)item).IsOwner())
						{
							return item;
						}
					}
				}
				end_IL_0001:;
			}
			catch (Exception ex)
			{
				ConfigEntry<bool> enableGrowthCurveDebug = SkillLimitExtenderPlugin.EnableGrowthCurveDebug;
				if (enableGrowthCurveDebug != null && enableGrowthCurveDebug.Value)
				{
					ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
					if (logger != null)
					{
						logger.LogDebug((object)("[SLE] Failed to resolve local player: " + ex.Message));
					}
				}
			}
			return null;
		}

		internal static bool TryGetSkillType(Skill skill, out SkillType skillType)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected I4, but got Unknown
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Expected I4, but got Unknown
			skillType = (SkillType)0;
			if (skill == null)
			{
				return false;
			}
			try
			{
				SkillDef value = Traverse.Create((object)skill).Field("m_info").GetValue<SkillDef>();
				if (value != null)
				{
					skillType = (SkillType)(int)Traverse.Create((object)value).Field("m_skill").GetValue<SkillType>();
					return (int)skillType != 0;
				}
				Player? safeLocalPlayer = GetSafeLocalPlayer();
				Skills val = ((safeLocalPlayer != null) ? ((Character)safeLocalPlayer).GetSkills() : null);
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				Dictionary<SkillType, Skill> value2 = Traverse.Create((object)val).Field("m_skillData").GetValue<Dictionary<SkillType, Skill>>();
				if (value2 == null)
				{
					return false;
				}
				foreach (KeyValuePair<SkillType, Skill> item in value2)
				{
					if (item.Value != skill)
					{
						continue;
					}
					skillType = (SkillType)(int)item.Key;
					return (int)skillType != 0;
				}
			}
			catch (Exception ex)
			{
				ConfigEntry<bool> enableGrowthCurveDebug = SkillLimitExtenderPlugin.EnableGrowthCurveDebug;
				if (enableGrowthCurveDebug != null && enableGrowthCurveDebug.Value)
				{
					ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
					if (logger != null)
					{
						logger.LogDebug((object)("[SLE] Failed to resolve skill type: " + ex.Message));
					}
				}
			}
			return false;
		}

		internal static SkillType GetSkillTypeOrNone(Skill skill)
		{
			//IL_000e: 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_0012: Unknown result type (might be due to invalid IL or missing references)
			SkillType skillType;
			return (SkillType)(TryGetSkillType(skill, out skillType) ? ((int)skillType) : 0);
		}
	}
	internal static class SLE_SkillsExtensions
	{
		private static readonly MethodInfo _miGetSkill = AccessTools.Method(typeof(Skills), "GetSkill", new Type[1] { typeof(SkillType) }, (Type[])null);

		private static readonly FieldInfo _fiSkillData = AccessTools.Field(typeof(Skills), "m_skillData");

		internal static Skill? GetSkillSafe(this Skills skills, SkillType st)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			//IL_0090: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			if ((Object)(object)skills == (Object)null)
			{
				return null;
			}
			if ((int)st == 0 || (int)st == 999)
			{
				return null;
			}
			if (_miGetSkill != null)
			{
				try
				{
					return (Skill)_miGetSkill.Invoke(skills, new object[1] { st });
				}
				catch
				{
				}
			}
			if (_fiSkillData != null)
			{
				try
				{
					if (_fiSkillData.GetValue(skills) is IDictionary<SkillType, Skill> dictionary && dictionary.TryGetValue(st, out var value))
					{
						return value;
					}
				}
				catch
				{
				}
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(Skills), "GetRandomSkillRange")]
	internal static class SLE_Skill_GetRandomSkillRange_Patch
	{
		[HarmonyPrefix]
		private static bool Prefix(Skills __instance, ref float min, ref float max, SkillType skillType)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			float skillFactor = __instance.GetSkillFactor(skillType);
			SLE_ExtendedScaling.GetDamageRange(skillFactor, out min, out max);
			return false;
		}
	}
	[HarmonyPatch(typeof(Skills), "GetRandomSkillFactor", new Type[] { typeof(SkillType) })]
	internal static class SLE_Skill_GetRandomSkillFactor_Patch
	{
		[HarmonyPrefix]
		private static bool Prefix(Skills __instance, SkillType skillType, ref float __result)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			float skillFactor = __instance.GetSkillFactor(skillType);
			SLE_ExtendedScaling.GetDamageRange(skillFactor, out var min, out var max);
			__result = Mathf.Lerp(min, max, Random.value);
			return false;
		}
	}
	internal static class SLE_SkillGrowthCalculator
	{
		internal static float GetRequirement(Skill skill, SkillType skillType)
		{
			//IL_0017: 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_003f: 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_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			if (skill == null)
			{
				return 1f;
			}
			if (!SkillConfigManager.UseCustomGrowthCurve(skillType))
			{
				return GetVanillaRequirement(skill.m_level);
			}
			float growthExponent = SkillConfigManager.GetGrowthExponent(skillType);
			float growthMultiplier = SkillConfigManager.GetGrowthMultiplier(skillType);
			float growthConstant = SkillConfigManager.GetGrowthConstant(skillType);
			float num = Mathf.Floor(skill.m_level + 1f);
			float num2 = Mathf.Pow(num, growthExponent) * growthMultiplier + growthConstant;
			if (float.IsNaN(num2) || float.IsInfinity(num2) || num2 <= 0f)
			{
				float vanillaRequirement = GetVanillaRequirement(skill.m_level);
				ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)($"[SLE Growth Curve] Invalid XP requirement for {skillType}: {num2}. " + $"Falling back to vanilla value {vanillaRequirement:F2}."));
				}
				return vanillaRequirement;
			}
			ConfigEntry<bool> enableGrowthCurveDebug = SkillLimitExtenderPlugin.EnableGrowthCurveDebug;
			if (enableGrowthCurveDebug != null && enableGrowthCurveDebug.Value)
			{
				float vanillaRequirement2 = GetVanillaRequirement(skill.m_level);
				ManualLogSource logger2 = SkillLimitExtenderPlugin.Logger;
				if (logger2 != null)
				{
					logger2.LogInfo((object)($"[SLE Growth Curve] Skill: {skillType}, Level: {skill.m_level:F1}, " + $"Mode: Custom, Params: exp={growthExponent:F2}, mult={growthMultiplier:F2}, const={growthConstant:F2}, " + $"Vanilla: {vanillaRequirement2:F2}, Custom: {num2:F2}"));
				}
			}
			return num2;
		}

		internal static float GetVanillaRequirement(float level)
		{
			return Mathf.Pow(Mathf.Floor(level + 1f), 1.5f) * 0.5f + 0.5f;
		}
	}
	[HarmonyPatch(typeof(Skill), "GetNextLevelRequirement")]
	internal static class SLE_Skill_GetNextLevelRequirement_Patch
	{
		[HarmonyPrefix]
		private static bool Prefix(Skill __instance, ref float __result)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (!SLE_SkillHelpers.TryGetSkillType(__instance, out var skillType))
			{
				return true;
			}
			if (!SkillConfigManager.UseCustomGrowthCurve(skillType))
			{
				return true;
			}
			__result = SLE_SkillGrowthCalculator.GetRequirement(__instance, skillType);
			return false;
		}
	}
	[HarmonyPatch(typeof(Skill), "GetLevelPercentage")]
	internal static class SLE_Skill_GetLevelPercentage_Patch
	{
		[HarmonyPrefix]
		private static bool Prefix(Skill __instance, ref float __result)
		{
			//IL_0019: 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)
			try
			{
				if (!SLE_SkillHelpers.TryGetSkillType(__instance, out var skillType))
				{
					return true;
				}
				int cap = SkillConfigManager.GetCap(skillType);
				if (__instance.m_level >= (float)cap)
				{
					__result = 0f;
					return false;
				}
				float requirement = SLE_SkillGrowthCalculator.GetRequirement(__instance, skillType);
				__result = ((requirement > 0f) ? Mathf.Clamp01(__instance.m_accumulator / requirement) : 0f);
				return false;
			}
			catch (Exception ex)
			{
				ConfigEntry<bool> enableGrowthCurveDebug = SkillLimitExtenderPlugin.EnableGrowthCurveDebug;
				if (enableGrowthCurveDebug != null && enableGrowthCurveDebug.Value)
				{
					ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
					if (logger != null)
					{
						logger.LogError((object)("[SLE Growth Curve] GetLevelPercentage patch failed: " + ex.Message));
					}
				}
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(Skill), "Raise")]
	internal static class SLE_Skill_Raise_Transpiler
	{
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Expected O, but got Unknown
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Expected O, but got Unknown
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Expected O, but got Unknown
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			MethodInfo methodInfo = AccessTools.Method(typeof(SLE_SkillHelpers), "GetSkillTypeOrNone", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(SkillConfigManager), "GetCap", (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
				if (logger != null)
				{
					logger.LogError((object)"[SLE] Skill.Raise transpiler failed: required helper method was not found.");
				}
				return list;
			}
			int num = 0;
			for (int i = 0; i < list.Count; i++)
			{
				CodeInstruction val = list[i];
				if (!(val.opcode != OpCodes.Ldc_R4) && val.operand is float num2 && !(Math.Abs(num2 - 100f) >= 0.0001f))
				{
					List<CodeInstruction> list2 = new List<CodeInstruction>
					{
						new CodeInstruction(OpCodes.Ldarg_0, (object)null),
						new CodeInstruction(OpCodes.Call, (object)methodInfo),
						new CodeInstruction(OpCodes.Call, (object)methodInfo2),
						new CodeInstruction(OpCodes.Conv_R4, (object)null)
					};
					list2[0].labels.AddRange(val.labels);
					list2[0].blocks.AddRange(val.blocks);
					list[i] = list2[0];
					list.InsertRange(i + 1, list2.GetRange(1, list2.Count - 1));
					i += list2.Count - 1;
					num++;
				}
			}
			if (num == 0)
			{
				ManualLogSource logger2 = SkillLimitExtenderPlugin.Logger;
				if (logger2 != null)
				{
					logger2.LogWarning((object)"[SLE] Skill.Raise transpiler found no level-100 constants to replace.");
				}
			}
			else
			{
				ManualLogSource logger3 = SkillLimitExtenderPlugin.Logger;
				if (logger3 != null)
				{
					logger3.LogInfo((object)$"[SLE] Skill.Raise: replaced {num} level-100 checks with configured skill caps.");
				}
			}
			return list;
		}
	}
	internal static class SLE_TerminalCommands
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleEvent <>9__0_0;

			public static ConsoleEvent <>9__0_1;

			internal void <Register>b__0_0(ConsoleEventArgs args)
			{
				SkillConfigManager.ReloadFromConfig();
				ZNet instance = ZNet.instance;
				if (instance != null && instance.IsServer())
				{
					SkillConfigManager.SendConfigToClientsIfChanged();
				}
				args.Context.AddString("SLE: configuration reloaded.");
			}

			internal void <Register>b__0_1(ConsoleEventArgs args)
			{
				args.Context.AddString("SLE config path: " + SkillConfigManager.GetConfigPath());
			}
		}

		public static void Register()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_006e: 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_0064: Expected O, but got Unknown
			try
			{
				object obj = <>c.<>9__0_0;
				if (obj == null)
				{
					ConsoleEvent val = delegate(ConsoleEventArgs args)
					{
						SkillConfigManager.ReloadFromConfig();
						ZNet instance = ZNet.instance;
						if (instance != null && instance.IsServer())
						{
							SkillConfigManager.SendConfigToClientsIfChanged();
						}
						args.Context.AddString("SLE: configuration reloaded.");
					};
					<>c.<>9__0_0 = val;
					obj = (object)val;
				}
				new ConsoleCommand("sle_config_reload", "Reload Skill Limit Extender BepInEx configuration", (ConsoleEvent)obj, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj2 = <>c.<>9__0_1;
				if (obj2 == null)
				{
					ConsoleEvent val2 = delegate(ConsoleEventArgs args)
					{
						args.Context.AddString("SLE config path: " + SkillConfigManager.GetConfigPath());
					};
					<>c.<>9__0_1 = val2;
					obj2 = (object)val2;
				}
				new ConsoleCommand("sle_config_path", "Show current Skill Limit Extender config path", (ConsoleEvent)obj2, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			}
			catch (Exception arg)
			{
				ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
				if (logger != null)
				{
					logger.LogError((object)$"[SLE] Failed to register console command: {arg}");
				}
			}
		}
	}
	internal static class SLE_VersionHandshake
	{
		internal static readonly List<ZRpc> ValidatedPeers = new List<ZRpc>();

		internal static void RPC_SLE_Version(ZRpc rpc, ZPackage pkg)
		{
			try
			{
				string text = pkg.ReadString();
				ZNet instance = ZNet.instance;
				bool flag = instance != null && instance.IsServer();
				ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
				if (logger != null)
				{
					logger.LogInfo((object)("[SLE] Version check: remote=" + text + ", local=1.3.4"));
				}
				if (text != "1.3.4")
				{
					if (flag)
					{
						ManualLogSource logger2 = SkillLimitExtenderPlugin.Logger;
						if (logger2 != null)
						{
							logger2.LogWarning((object)"[SLE] Incompatible client version; disconnecting");
						}
						rpc.Invoke("Error", new object[1] { 3 });
					}
					else
					{
						ManualLogSource logger3 = SkillLimitExtenderPlugin.Logger;
						if (logger3 != null)
						{
							logger3.LogWarning((object)"[SLE] Server version differs; features may be limited");
						}
					}
				}
				else if (flag)
				{
					ValidatedPeers.Add(rpc);
				}
				else
				{
					ManualLogSource logger4 = SkillLimitExtenderPlugin.Logger;
					if (logger4 != null)
					{
						logger4.LogInfo((object)"[SLE] Server and client versions match");
					}
				}
			}
			catch (EndOfStreamException ex)
			{
				ManualLogSource logger5 = SkillLimitExtenderPlugin.Logger;
				if (logger5 != null)
				{
					logger5.LogError((object)("[SLE] Version handshake failed - incompatible data format: " + ex.Message));
				}
				rpc.Invoke("Error", new object[1] { 3 });
			}
			catch (Exception arg)
			{
				ManualLogSource logger6 = SkillLimitExtenderPlugin.Logger;
				if (logger6 != null)
				{
					logger6.LogError((object)$"[SLE] Version handshake error: {arg}");
				}
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
	internal static class SLE_VersionHandshake_OnNewConnection
	{
		[HarmonyPrefix]
		private static void Prefix(ZNetPeer peer, ZNet __instance)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			peer.m_rpc.Register<ZPackage>("SLE_Version", (Action<ZRpc, ZPackage>)SLE_VersionHandshake.RPC_SLE_Version);
			ZPackage val = new ZPackage();
			val.Write("1.3.4");
			peer.m_rpc.Invoke("SLE_Version", new object[1] { val });
		}
	}
	[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
	internal static class SLE_VersionHandshake_VerifyClient
	{
		[HarmonyPrefix]
		private static bool Prefix(ZRpc rpc, ZPackage pkg, ZNet __instance)
		{
			if (__instance.IsServer() && !SLE_VersionHandshake.ValidatedPeers.Contains(rpc))
			{
				ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)"[SLE] Peer never sent version; disconnecting");
				}
				rpc.Invoke("Error", new object[1] { 3 });
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(ZNet), "Disconnect")]
	internal static class SLE_VersionHandshake_RemovePeerOnDisconnect
	{
		[HarmonyPrefix]
		private static void Prefix(ZNetPeer peer, ZNet __instance)
		{
			if (__instance.IsServer())
			{
				ZRpc val = peer?.m_rpc;
				if (val != null)
				{
					SLE_VersionHandshake.ValidatedPeers.Remove(val);
				}
			}
		}
	}
	internal static class SkillConfigManager
	{
		private sealed class SkillSettings
		{
			internal readonly string Section;

			internal readonly ConfigEntry<int> Cap;

			internal readonly ConfigEntry<int> BonusCap;

			internal readonly ConfigEntry<bool> Relative;

			internal readonly ConfigEntry<bool> UseCustomGrowthCurve;

			internal readonly ConfigEntry<float> GrowthExponent;

			internal readonly ConfigEntry<float> GrowthMultiplier;

			internal readonly ConfigEntry<float> GrowthConstant;

			internal SkillSettings(ConfigFile config, string section)
			{
				Section = section;
				Cap = config.Bind<int>(section, "Cap", 1000, "Maximum level this skill can reach. Formula: finalLevel <= Cap.");
				BonusCap = config.Bind<int>(section, "BonusCap", 500, "Maximum skill-effect factor expressed as a level-like percentage. 100 = factor 1.0, 250 = factor 2.5, 500 = factor 5.0. Formula: maxSkillFactor = BonusCap / 100.");
				Relative = config.Bind<bool>(section, "Relative", false, "Controls how the skill-effect factor is calculated. true: the configured Cap becomes the point where BonusCap is reached. false: every 100 skill levels add 1.0 factor until BonusCap is reached. Formula (true): factor = (level / Cap) * (BonusCap / 100). Formula (false): factor = level / 100. Both modes clamp to BonusCap / 100.");
				UseCustomGrowthCurve = config.Bind<bool>(section, "UseCustomGrowthCurve", true, "Use the custom XP requirement formula below instead of vanilla XP progression.");
				GrowthExponent = config.Bind<float>(section, "GrowthExponent", 2.1f, "Exponent used only for XP required to gain the next level. Vanilla = 1.5. Final formula: XP = nextLevel^GrowthExponent * GrowthMultiplier + GrowthConstant.");
				GrowthMultiplier = config.Bind<float>(section, "GrowthMultiplier", 0.04f, "Multiplier used only for XP required to gain the next level. Vanilla = 0.5. Final formula: XP = nextLevel^GrowthExponent * GrowthMultiplier + GrowthConstant.");
				GrowthConstant = config.Bind<float>(section, "GrowthConstant", 8f, "Constant added only to XP required to gain the next level. Vanilla = 0.5. Final formula: XP = nextLevel^GrowthExponent * GrowthMultiplier + GrowthConstant.");
			}
		}

		private sealed class SkillValues
		{
			internal int Cap;

			internal int BonusCap;

			internal bool Relative;

			internal bool UseCustomGrowthCurve;

			internal float GrowthExponent;

			internal float GrowthMultiplier;

			internal float GrowthConstant;
		}

		internal const int DefaultCapFallback = 1000;

		internal const int DefaultBonusCapFallback = 500;

		private static readonly Dictionary<string, SkillSettings> EntriesByName = new Dictionary<string, SkillSettings>(StringComparer.OrdinalIgnoreCase);

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

		private static Dictionary<string, SkillValues> _serverEntries = new Dictionary<string, SkillValues>(StringComparer.OrdinalIgnoreCase);

		private static readonly HashSet<int> WarnedSkillIds = new HashSet<int>();

		private static ConfigFile _config = null;

		private static bool _serverInitialized;

		private static bool _skillsInitialized;

		private static bool _isServerConfig;

		private static string _lastSnapshotHash = string.Empty;

		internal static ConfigEntry<bool> ServerConfigLocked { get; private set; } = null;

		internal static void InitializeServer(ConfigFile config)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			if (!_serverInitialized)
			{
				_config = config;
				ServerConfigLocked = config.Bind<bool>("2 - Server", "LockConfiguration", false, new ConfigDescription("If true, the server sends its Skill Limit Extender gameplay configuration to clients.", (AcceptableValueBase)null, new object[1]
				{
					new ConfigurationManagerAttributes
					{
						Category = "2 - Server",
						Order = -1000,
						IsAdminOnly = true
					}
				}));
				_serverInitialized = true;
			}
		}

		internal static void InitializeSkills()
		{
			if (!_skillsInitialized)
			{
				if (!_serverInitialized)
				{
					throw new InvalidOperationException("SkillConfigManager.InitializeServer must be called before InitializeSkills.");
				}
				BindVanillaSkills();
				_skillsInitialized = true;
				ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
				if (logger != null)
				{
					logger.LogInfo((object)$"[SLE] BepInEx skill configuration initialized ({EntriesByName.Count} skill sections)");
				}
			}
		}

		internal static void Initialize(ConfigFile config)
		{
			InitializeServer(config);
			InitializeSkills();
		}

		private static void BindVanillaSkills()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Invalid comparison between Unknown and I4
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected I4, but got Unknown
			foreach (SkillType value in Enum.GetValues(typeof(SkillType)))
			{
				if ((int)value != 0 && (int)value != 999)
				{
					string text = ((object)value/*cast due to .constrained prefix*/).ToString();
					if (!int.TryParse(text, out var _))
					{
						BindSkill(text);
						SkillKeysById[(int)value] = text;
					}
				}
			}
		}

		private static SkillSettings BindSkill(string section)
		{
			section = NormalizeSectionName(section);
			if (EntriesByName.TryGetValue(section, out SkillSettings value))
			{
				return value;
			}
			SkillSettings skillSettings = new SkillSettings(_config, section);
			EntriesByName[section] = skillSettings;
			return skillSettings;
		}

		private static SkillSettings GetSettings(SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			string skillKeyForLookup = GetSkillKeyForLookup(skillType);
			return BindSkill(skillKeyForLookup);
		}

		private unsafe static string GetSkillKeyForLookup(SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Expected I4, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			int num = (int)skillType;
			if (SkillKeysById.TryGetValue(num, out string value))
			{
				return value;
			}
			string text = ((object)(*(SkillType*)(&skillType))/*cast due to .constrained prefix*/).ToString();
			if (!int.TryParse(text, out var result))
			{
				SkillKeysById[num] = text;
				return text;
			}
			string actualModSkillName = GetActualModSkillName(skillType);
			string text2 = NormalizeSectionName(actualModSkillName);
			if (string.IsNullOrWhiteSpace(text2) || int.TryParse(text2, out result))
			{
				text2 = text;
			}
			SkillKeysById[num] = text2;
			if (!EntriesByName.ContainsKey(text2) && WarnedSkillIds.Add(num))
			{
				ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
				if (logger != null)
				{
					logger.LogInfo((object)$"[SLE] Discovered MOD skill {num}; using config section [{text2}]");
				}
			}
			return text2;
		}

		private unsafe static string GetActualModSkillName(SkillType skillType)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Player safeLocalPlayer = SLE_SkillHelpers.GetSafeLocalPlayer();
				Skills val = ((safeLocalPlayer != null) ? ((Character)safeLocalPlayer).GetSkills() : null);
				if ((Object)(object)val == (Object)null)
				{
					return ((object)(*(SkillType*)(&skillType))/*cast due to .constrained prefix*/).ToString();
				}
				Dictionary<SkillType, Skill> value = Traverse.Create((object)val).Field("m_skillData").GetValue<Dictionary<SkillType, Skill>>();
				if (value == null || !value.TryGetValue(skillType, out var value2) || value2 == null)
				{
					return ((object)(*(SkillType*)(&skillType))/*cast due to .constrained prefix*/).ToString();
				}
				SkillDef value3 = Traverse.Create((object)value2).Field("m_info").GetValue<SkillDef>();
				if (value3 == null)
				{
					return ((object)(*(SkillType*)(&skillType))/*cast due to .constrained prefix*/).ToString();
				}
				string value4 = Traverse.Create((object)value3).Field("m_description").GetValue<string>();
				if (!string.IsNullOrWhiteSpace(value4))
				{
					if (!value4.StartsWith("$"))
					{
						return value4;
					}
					Localization instance = Localization.instance;
					string text = ((instance != null) ? instance.Localize(value4) : null);
					if (!string.IsNullOrWhiteSpace(text) && text != value4)
					{
						return text;
					}
				}
			}
			catch (Exception ex)
			{
				ConfigEntry<bool> enableGrowthCurveDebug = SkillLimitExtenderPlugin.EnableGrowthCurveDebug;
				if (enableGrowthCurveDebug != null && enableGrowthCurveDebug.Value)
				{
					ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
					if (logger != null)
					{
						logger.LogDebug((object)("[SLE] MOD skill name lookup failed: " + ex.Message));
					}
				}
			}
			return ((object)(*(SkillType*)(&skillType))/*cast due to .constrained prefix*/).ToString();
		}

		private static string NormalizeSectionName(string name)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				return "UnknownSkill";
			}
			StringBuilder stringBuilder = new StringBuilder(name.Length);
			foreach (char c in name)
			{
				if (char.IsLetterOrDigit(c) || c == '_' || c == '-')
				{
					stringBuilder.Append(c);
				}
			}
			return (stringBuilder.Length > 0) ? stringBuilder.ToString() : "UnknownSkill";
		}

		private static bool TryGetServerValues(string section, out SkillValues values)
		{
			values = null;
			return _isServerConfig && _serverEntries.TryGetValue(section, out values);
		}

		internal static int GetCap(SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			SkillSettings settings = GetSettings(skillType);
			if (TryGetServerValues(settings.Section, out SkillValues values) && values.Cap > 0)
			{
				return values.Cap;
			}
			return (settings.Cap.Value > 0) ? settings.Cap.Value : 1000;
		}

		internal static int GetBonusCap(SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			SkillSettings settings = GetSettings(skillType);
			if (TryGetServerValues(settings.Section, out SkillValues values) && values.BonusCap > 0)
			{
				return values.BonusCap;
			}
			return (settings.BonusCap.Value > 0) ? settings.BonusCap.Value : 500;
		}

		internal static bool IsRelative(SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			SkillSettings settings = GetSettings(skillType);
			SkillValues values;
			return TryGetServerValues(settings.Section, out values) ? values.Relative : settings.Relative.Value;
		}

		internal static bool UseCustomGrowthCurve(SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			SkillSettings settings = GetSettings(skillType);
			SkillValues values;
			return TryGetServerValues(settings.Section, out values) ? values.UseCustomGrowthCurve : settings.UseCustomGrowthCurve.Value;
		}

		internal static float GetGrowthExponent(SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			SkillSettings settings = GetSettings(skillType);
			SkillValues values;
			return TryGetServerValues(settings.Section, out values) ? values.GrowthExponent : settings.GrowthExponent.Value;
		}

		internal static float GetGrowthMultiplier(SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			SkillSettings settings = GetSettings(skillType);
			SkillValues values;
			return TryGetServerValues(settings.Section, out values) ? values.GrowthMultiplier : settings.GrowthMultiplier.Value;
		}

		internal static float GetGrowthConstant(SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			SkillSettings settings = GetSettings(skillType);
			SkillValues values;
			return TryGetServerValues(settings.Section, out values) ? values.GrowthConstant : settings.GrowthConstant.Value;
		}

		internal static int GetCartographySkillCap()
		{
			string[] array = new string[3] { "Cartography", "CartographySkill", "1337" };
			foreach (string key in array)
			{
				if (_isServerConfig && _serverEntries.TryGetValue(key, out SkillValues value) && value.Cap > 0)
				{
					return value.Cap;
				}
				if (EntriesByName.TryGetValue(key, out SkillSettings value2) && value2.Cap.Value > 0)
				{
					return value2.Cap.Value;
				}
			}
			return 1000;
		}

		internal static bool IsConfiguredSkill(SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			string skillKeyForLookup = GetSkillKeyForLookup(skillType);
			return EntriesByName.ContainsKey(skillKeyForLookup) || _serverEntries.ContainsKey(skillKeyForLookup);
		}

		internal static int GetCapByName(string name)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Invalid comparison between Unknown and I4
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrWhiteSpace(name))
			{
				return 1000;
			}
			if (Enum.TryParse<SkillType>(name, ignoreCase: true, out SkillType result) && (int)result != 0 && (int)result != 999)
			{
				return GetCap(result);
			}
			string key = NormalizeSectionName(name);
			if (_isServerConfig && _serverEntries.TryGetValue(key, out SkillValues value) && value.Cap > 0)
			{
				return value.Cap;
			}
			if (EntriesByName.TryGetValue(key, out SkillSettings value2) && value2.Cap.Value > 0)
			{
				return value2.Cap.Value;
			}
			return 1000;
		}

		internal static int GetSkillLimit(SkillType skillType)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return GetCap(skillType);
		}

		internal static float GetFactorDenominator(SkillType skillType)
		{
			return 100f;
		}

		internal static float GetUiDenominator()
		{
			return Math.Max(1, 1000);
		}

		internal static float GetUiDenominatorForSkill(SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return Math.Max(1, GetCap(skillType));
		}

		internal static float GetUiDenominatorForSkillSafe(object? skillMaybe)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Skill val = (Skill)((skillMaybe is Skill) ? skillMaybe : null);
				if (val != null && SLE_SkillHelpers.TryGetSkillType(val, out var skillType))
				{
					return GetUiDenominatorForSkill(skillType);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)("[SLE] GetUiDenominatorForSkillSafe failed: " + ex.Message));
				}
			}
			return GetUiDenominator();
		}

		internal static void ReloadFromConfig()
		{
			try
			{
				_config.Reload();
				ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
				if (logger != null)
				{
					logger.LogInfo((object)"[SLE] BepInEx configuration reloaded");
				}
			}
			catch (Exception arg)
			{
				ManualLogSource logger2 = SkillLimitExtenderPlugin.Logger;
				if (logger2 != null)
				{
					logger2.LogError((object)$"[SLE] Config reload failed: {arg}");
				}
			}
		}

		internal static string GetConfigPath()
		{
			return _config.ConfigFilePath;
		}

		internal static void OnConfigReceivedStatic(long sender, string snapshot, int protocolVersion)
		{
			ZNet instance = ZNet.instance;
			if (instance != null && instance.IsServer())
			{
				return;
			}
			if (!VersionInfo.IsCompatible(protocolVersion))
			{
				ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)$"[SLE] Config protocol mismatch: remote={protocolVersion}, local={6}");
				}
				return;
			}
			try
			{
				Dictionary<string, SkillValues> dictionary = new Dictionary<string, SkillValues>(StringComparer.OrdinalIgnoreCase);
				string record = string.Empty;
				string[] array = (snapshot ?? string.Empty).Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
				foreach (string text in array)
				{
					string text2 = text.TrimEnd(new char[1] { '\r' });
					string[] array2 = text2.Split(new char[1] { '|' });
					if (array2.Length != 0)
					{
						if (array2[0] == "S" && array2.Length == 9)
						{
							string key = Decode(array2[1]);
							dictionary[key] = new SkillValues
							{
								Cap = ParseInt(array2[2], 1000),
								BonusCap = ParseInt(array2[3], 500),
								Relative = ParseBool(array2[4], fallback: false),
								UseCustomGrowthCurve = ParseBool(array2[5], fallback: true),
								GrowthExponent = ParseFloat(array2[6], 2.1f),
								GrowthMultiplier = ParseFloat(array2[7], 0.04f),
								GrowthConstant = ParseFloat(array2[8], 8f)
							};
						}
						else if (array2[0] == "E")
						{
							record = text2;
						}
					}
				}
				_serverEntries = dictionary;
				_isServerConfig = true;
				SLE_ExtendedScaling.ApplyServerRecord(record);
				ManualLogSource logger2 = SkillLimitExtenderPlugin.Logger;
				if (logger2 != null)
				{
					logger2.LogInfo((object)$"[SLE] Received server configuration ({_serverEntries.Count} skill sections)");
				}
			}
			catch (Exception arg)
			{
				ManualLogSource logger3 = SkillLimitExtenderPlugin.Logger;
				if (logger3 != null)
				{
					logger3.LogError((object)$"[SLE] Failed to apply server config: {arg}");
				}
			}
		}

		internal static void SendConfigToClients()
		{
			ZNet instance = ZNet.instance;
			if (instance == null || !instance.IsServer())
			{
				return;
			}
			ConfigEntry<bool> serverConfigLocked = ServerConfigLocked;
			if (serverConfigLocked == null || !serverConfigLocked.Value)
			{
				return;
			}
			try
			{
				string text = BuildSnapshot();
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "SLE_ConfigSync", new object[2] { text, 6 });
				_lastSnapshotHash = ComputeHash(text);
				ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
				if (logger != null)
				{
					logger.LogInfo((object)$"[SLE] Server configuration sent to clients ({text.Length} chars)");
				}
			}
			catch (Exception arg)
			{
				ManualLogSource logger2 = SkillLimitExtenderPlugin.Logger;
				if (logger2 != null)
				{
					logger2.LogError((object)$"[SLE] Failed to send server config: {arg}");
				}
			}
		}

		internal static void SendConfigToClientsIfChanged()
		{
			ZNet instance = ZNet.instance;
			if (instance == null || !instance.IsServer())
			{
				return;
			}
			ConfigEntry<bool> serverConfigLocked = ServerConfigLocked;
			if (serverConfigLocked == null || !serverConfigLocked.Value)
			{
				return;
			}
			try
			{
				string text = BuildSnapshot();
				string text2 = ComputeHash(text);
				if (!string.Equals(text2, _lastSnapshotHash, StringComparison.Ordinal))
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(0L, "SLE_ConfigSync", new object[2] { text, 6 });
					_lastSnapshotHash = text2;
					ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
					if (logger != null)
					{
						logger.LogInfo((object)"[SLE] Changed server configuration broadcasted to clients");
					}
				}
			}
			catch (Exception arg)
			{
				ManualLogSource logger2 = SkillLimitExtenderPlugin.Logger;
				if (logger2 != null)
				{
					logger2.LogError((object)$"[SLE] Failed to broadcast server config: {arg}");
				}
			}
		}

		private static string BuildSnapshot()
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (KeyValuePair<string, SkillSettings> item in EntriesByName.OrderBy<KeyValuePair<string, SkillSettings>, string>((KeyValuePair<string, SkillSettings> p) => p.Key, StringComparer.OrdinalIgnoreCase))
			{
				SkillSettings value = item.Value;
				stringBuilder.Append("S|").Append(Encode(value.Section)).Append('|')
					.Append(value.Cap.Value)
					.Append('|')
					.Append(value.BonusCap.Value)
					.Append('|')
					.Append(value.Relative.Value ? "1" : "0")
					.Append('|')
					.Append(value.UseCustomGrowthCurve.Value ? "1" : "0")
					.Append('|')
					.Append(value.GrowthExponent.Value.ToString("R", CultureInfo.InvariantCulture))
					.Append('|')
					.Append(value.GrowthMultiplier.Value.ToString("R", CultureInfo.InvariantCulture))
					.Append('|')
					.Append(value.GrowthConstant.Value.ToString("R", CultureInfo.InvariantCulture))
					.Append('\n');
			}
			stringBuilder.Append(SLE_ExtendedScaling.BuildSyncRecord()).Append('\n');
			return stringBuilder.ToString();
		}

		private static string Encode(string value)
		{
			return Convert.ToBase64String(Encoding.UTF8.GetBytes(value ?? string.Empty));
		}

		private static string Decode(string value)
		{
			return Encoding.UTF8.GetString(Convert.FromBase64String(value));
		}

		private static int ParseInt(string value, int fallback)
		{
			int result;
			return int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result) ? result : fallback;
		}

		private static float ParseFloat(string value, float fallback)
		{
			float result;
			return float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out result) ? result : fallback;
		}

		private static bool ParseBool(string value, bool fallback)
		{
			if (value == "1")
			{
				return true;
			}
			if (value == "0")
			{
				return false;
			}
			bool result;
			return bool.TryParse(value, out result) ? result : fallback;
		}

		private static string ComputeHash(string text)
		{
			using SHA256 sHA = SHA256.Create();
			byte[] array = sHA.ComputeHash(Encoding.UTF8.GetBytes(text ?? string.Empty));
			return BitConverter.ToString(array).Replace("-", string.Empty);
		}
	}
	[BepInPlugin("SkillLimitExtender", "SkillLimitExtender", "1.3.4")]
	public class SkillLimitExtenderPlugin : BaseUnityPlugin
	{
		internal const string PluginGuid = "SkillLimitExtender";

		internal const string PluginName = "SkillLimitExtender";

		internal const string PluginVersion = "1.3.4";

		private readonly Harmony _harmony = new Harmony("SkillLimitExtender");

		internal static ManualLogSource Logger { get; private set; }

		internal static ConfigEntry<bool> EnableGrowthCurveDebug { get; private set; }

		private void Awake()
		{
			Logger = ((BaseUnityPlugin)this).Logger;
			EnableGrowthCurveDebug = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Debug", "Enable Growth Curve Debug", false, "Enable debug logging for skill growth curve calculations");
			try
			{
				Logger.LogInfo((object)("[SLE] " + VersionInfo.VersionString));
				SkillConfigManager.InitializeServer(((BaseUnityPlugin)this).Config);
				SLE_ExtendedScaling.Initialize(((BaseUnityPlugin)this).Config);
				SkillConfigManager.InitializeSkills();
				WarnAboutLegacyYaml();
				_harmony.PatchAll(typeof(SkillLimitExtenderPlugin).Assembly);
				SLE_Hook_ModSkills.Initialize(_harmony);
				SLE_TerminalCommands.Register();
				Logger.LogInfo((object)"[SLE] Plugin loaded successfully (v1.3.4)");
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"[SLE] Awake failed: {arg}");
			}
		}

		private static void WarnAboutLegacyYaml()
		{
			string text = Path.Combine(Paths.ConfigPath, "SkillLimitExtender", "SLE_Skill_List.yaml");
			if (File.Exists(text))
			{
				Logger.LogWarning((object)("[SLE] Legacy YAML config detected and ignored: " + text + ". Skill settings now live in the normal BepInEx SkillLimitExtender.cfg file."));
			}
		}

		private void Start()
		{
			if (!((Object)(object)ZNet.instance != (Object)null))
			{
				return;
			}
			try
			{
				ZRoutedRpc.instance.Register<string, int>("SLE_ConfigSync", (Action<long, string, int>)SkillConfigManager.OnConfigReceivedStatic);
				ManualLogSource logger = Logger;
				if (logger != null)
				{
					logger.LogInfo((object)"[SLE] RPC registered successfully");
				}
			}
			catch (Exception arg)
			{
				ManualLogSource logger2 = Logger;
				if (logger2 != null)
				{
					logger2.LogError((object)$"[SLE] RPC registration failed: {arg}");
				}
			}
		}

		private void OnDestroy()
		{
			try
			{
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"[SLE] UnpatchSelf failed: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	internal static class SLE_Hook_PlayerSpawned
	{
		[HarmonyPostfix]
		private static void Postfix(Player __instance)
		{
			ZNet instance = ZNet.instance;
			if (instance != null && instance.IsServer() && (Object)(object)__instance != (Object)null)
			{
				SkillConfigManager.SendConfigToClientsIfChanged();
			}
		}
	}
	internal static class SLE_Hook_ModSkills
	{
		private static Harmony? _harmony;

		private static readonly List<string> _patchedAssemblies = new List<string>();

		internal static void Initialize(Harmony harmony)
		{
			_harmony = harmony;
			ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
			if (logger != null)
			{
				logger.LogInfo((object)"[SLE] ModSkills: Starting universal MOD skill patch system...");
			}
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				int num = 0;
				Assembly[] array = assemblies;
				foreach (Assembly assembly in array)
				{
					try
					{
						if (IsModSkillAssembly(assembly))
						{
							int num2 = PatchModSkillAssembly(assembly);
							if (num2 > 0)
							{
								num += num2;
								_patchedAssemblies.Add(assembly.GetName().Name);
							}
						}
					}
					catch (Exception ex)
					{
						ConfigEntry<bool> enableGrowthCurveDebug = SkillLimitExtenderPlugin.EnableGrowthCurveDebug;
						if (enableGrowthCurveDebug != null && enableGrowthCurveDebug.Value)
						{
							ManualLogSource logger2 = SkillLimitExtenderPlugin.Logger;
							if (logger2 != null)
							{
								logger2.LogDebug((object)("[SLE] ModSkills: Skipping assembly " + assembly.GetName().Name + ": " + ex.Message));
							}
						}
					}
				}
				ManualLogSource logger3 = SkillLimitExtenderPlugin.Logger;
				if (logger3 != null)
				{
					logger3.LogInfo((object)$"[SLE] ModSkills: Successfully patched {num} methods across {_patchedAssemblies.Count} MOD assemblies");
				}
				if (_patchedAssemblies.Count > 0)
				{
					ManualLogSource logger4 = SkillLimitExtenderPlugin.Logger;
					if (logger4 != null)
					{
						logger4.LogInfo((object)("[SLE] ModSkills: Patched assemblies: " + string.Join(", ", _patchedAssemblies)));
					}
				}
			}
			catch (Exception arg)
			{
				ManualLogSource logger5 = SkillLimitExtenderPlugin.Logger;
				if (logger5 != null)
				{
					logger5.LogError((object)$"[SLE] ModSkills: Initialization failed: {arg}");
				}
			}
		}

		private static bool IsModSkillAssembly(Assembly assembly)
		{
			try
			{
				string name = assembly.GetName().Name;
				if (name.StartsWith("Assembly-CSharp") || name.StartsWith("UnityEngine") || name.StartsWith("System") || name.StartsWith("mscorlib") || name.StartsWith("BepInEx") || name.StartsWith("0Harmony") || name.StartsWith("Jotunn") || name == "SkillLimitExtender")
				{
					return false;
				}
				Type[] types = assembly.GetTypes();
				Type[] array = types;
				foreach (Type type in array)
				{
					if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length == 0)
					{
						continue;
					}
					MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					MethodInfo[] array2 = methods;
					foreach (MethodInfo methodInfo in array2)
					{
						if (methodInfo.Name.Contains("Skill") || methodInfo.Name.Contains("Raise") || methodInfo.Name.Contains("Cheat"))
						{
							return true;
						}
					}
				}
				return false;
			}
			catch
			{
				return false;
			}
		}

		private static int PatchModSkillAssembly(Assembly assembly)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			int num = 0;
			try
			{
				Type[] types = assembly.GetTypes();
				Type[] array = types;
				foreach (Type type in array)
				{
					MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					MethodInfo[] array2 = methods;
					foreach (MethodInfo methodInfo in array2)
					{
						if (!IsSkillLimitMethod(methodInfo))
						{
							continue;
						}
						try
						{
							HarmonyMethod val = new HarmonyMethod(typeof(SLE_Hook_ModSkills), "UniversalSkillLimitTranspiler", (Type[])null);
							Harmony? harmony = _harmony;
							if (harmony != null)
							{
								harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null);
							}
							ManualLogSource logger = SkillLimitExtenderPlugin.Logger;
							if (logger != null)
							{
								logger.LogInfo((object)("[SLE] ModSkills: Patched " + assembly.GetName().Name + "." + type.Name + "." + methodInfo.Name));
							}
							num++;
						}
						catch (Exception ex)
						{
							ManualLogSource logger2 = SkillLimitExtenderPlugin.Logger;
							if (logger2 != null)
							{
								logger2.LogWarning((object)("[SLE] ModSkills: Failed to patch " + type.Name + "." + methodInfo.Name + ": " + ex.Message));
							}
						}
					}
				}
			}
			catch (Exception arg)
			{
				ManualLogSource logger3 = SkillLimitExtenderPlugin.Logger;
				if (logger3 != null)
				{
					logger3.LogError((object)$"[SLE] ModSkills: Error processing assembly {assembly.GetName().Name}: {arg}");
				}
			}
			return num;
		}

		private static bool IsSkillLimitMethod(MethodInfo method)
		{
			try
			{
				if (!method.Name.Contains("Skill") && !method.Name.Contains("Raise") && !method.Name.Contains("Cheat") && !method.Name.Contains("Level"))
				{
					return false;
				}
				List<CodeInstruction> currentInstructions = PatchProcessor.GetCurrentInstructions((MethodBase)method, int.MaxValue, (ILGenerator)null);
				if (currentInstructions.Any((CodeInstruction instr) => instr.opcode == OpCodes.Ldc_R4 && instr.operand is float num && Math.Abs(num - 100f) < 0.001f))
				{
					return currentInstructions.Any(delegate(CodeInstruction instr)
					{
						int result;
						if (instr.opcode == OpCodes.Call)
						{
							object operand = instr.operand;
							result = ((operand != null && operand.ToString()?.Contains("Clamp") == true) ? 1 : 0);
						}
						else
						{
							result = 0;
						}
						return (byte)result != 0;
					});
				}
				return false;
			}
			catch
			{
				return false;
			}
		}

		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> UniversalSkillLimitTranspiler(IEnumerable<CodeInstruction> instructions, MethodBase original)
		{
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Expected O, but got Unknown
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			try
			{
				for (int i = 0; i < list.Count; i++)
				{
					CodeInstruction val = list[i];
					if (!(val.opcode == OpCodes.Ldc_R4) || !(val.operand is float num) || !(Math.Abs(num - 100f) < 0.001f))
					{
						continue;
					}
					bool flag = false;
					for (int j = Math.Max(0, i - 5); j < Math.Min(list.Count, i + 5); j++)
					{
						if (list[j].opcode == OpCodes.Call)
						{
							object operand = list[j].operand;
							if (operand != null && operand.ToString()?.Contains("Clamp") == true)
							{
								flag = true;
								break;
							}
						}
					}
					if (flag)
					{
						List<CodeInst