Decompiled source of Skuld v1.0.0

Skuld.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("Skuld")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Converts death skill loss into repayable per-skill debt.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+9b5c5156c205ee9ba7217ec92501947c6fcacbb8")]
[assembly: AssemblyProduct("Skuld")]
[assembly: AssemblyTitle("Skuld")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Skuld
{
	internal static class ModColorUtil
	{
		internal static Color Parse(string value, Color fallback)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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 (string.IsNullOrWhiteSpace(value))
			{
				return fallback;
			}
			string value2 = value.Trim();
			if (TryParseHex(value2, out var color))
			{
				return color;
			}
			if (TryParseRgb(value2, out var color2))
			{
				return color2;
			}
			Plugin.Log.LogWarning((object)("Skuld: could not parse color '" + value + "'. Using default."));
			return fallback;
		}

		private static bool TryParseHex(string value, out Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			color = default(Color);
			string text = (value.StartsWith("#", StringComparison.Ordinal) ? value.Substring(1) : value);
			if (text.Length != 6 && text.Length != 8)
			{
				return false;
			}
			if (!uint.TryParse(text, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result))
			{
				return false;
			}
			if (text.Length == 6)
			{
				color = new Color((float)((result >> 16) & 0xFF) / 255f, (float)((result >> 8) & 0xFF) / 255f, (float)(result & 0xFF) / 255f, 1f);
				return true;
			}
			color = new Color((float)((result >> 24) & 0xFF) / 255f, (float)((result >> 16) & 0xFF) / 255f, (float)((result >> 8) & 0xFF) / 255f, (float)(result & 0xFF) / 255f);
			return true;
		}

		private static bool TryParseRgb(string value, out Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			color = default(Color);
			string[] array = value.Split(new char[1] { ',' });
			if (array.Length < 3 || array.Length > 4)
			{
				return false;
			}
			if (!TryParseByte(array[0], out var result) || !TryParseByte(array[1], out var result2) || !TryParseByte(array[2], out var result3))
			{
				return false;
			}
			float num = 1f;
			if (array.Length == 4)
			{
				if (!TryParseByte(array[3], out var result4))
				{
					return false;
				}
				num = (float)(int)result4 / 255f;
			}
			color = new Color((float)(int)result / 255f, (float)(int)result2 / 255f, (float)(int)result3 / 255f, num);
			return true;
		}

		private static bool TryParseByte(string value, out byte result)
		{
			if (int.TryParse(value.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2))
			{
				result = (byte)Mathf.Clamp(result2, 0, 255);
				return true;
			}
			result = 0;
			return false;
		}
	}
	internal static class ModConfig
	{
		private static readonly Color DefaultDebtBarColor = new Color(0.55f, 0.12f, 0.22f, 1f);

		private static readonly Color DefaultDebtTextColor = new Color(0.85f, 0.18f, 0.18f, 1f);

		private const int DefaultDebtTextSize = 18;

		private const string ServerSyncedNote = " This setting is server synced.";

		private const string ClientOnlyNote = " Client-only; not server synced.";

		private static bool serverOverlayActive;

		private static bool serverEnableMod;

		private static float serverDebtPaydownShare;

		private static int serverMaxDebtPerSkill;

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

		internal static ConfigEntry<float> DebtPaydownShare { get; private set; } = null;

		internal static ConfigEntry<int> MaxDebtPerSkill { get; private set; } = null;

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

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

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

		internal static ConfigEntry<string> DebtBarColor { get; private set; } = null;

		internal static ConfigEntry<string> DebtTextColor { get; private set; } = null;

		internal static ConfigEntry<int> DebtTextSize { get; private set; } = null;

		private static bool UseServerOverlay
		{
			get
			{
				if (serverOverlayActive && (Object)(object)ZNet.instance != (Object)null)
				{
					return !ZNet.instance.IsServer();
				}
				return false;
			}
		}

		internal static bool IsModEnabled
		{
			get
			{
				if (!UseServerOverlay)
				{
					return EnableMod.Value;
				}
				return serverEnableMod;
			}
		}

		internal static void ApplyServerOverlay(bool enableMod, float paydownShare, int maxDebtPerSkill)
		{
			serverOverlayActive = true;
			serverEnableMod = enableMod;
			serverDebtPaydownShare = paydownShare;
			serverMaxDebtPerSkill = maxDebtPerSkill;
		}

		internal static void ClearServerOverlay()
		{
			serverOverlayActive = false;
		}

		internal static void Bind(ConfigFile config)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Expected O, but got Unknown
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			EnableMod = config.Bind<bool>("General", "EnableMod", true, "Enable Skuld debt mechanics. This setting is server synced.");
			DebtPaydownShare = config.Bind<float>("General", "DebtPaydownShare", 0.5f, new ConfigDescription("Fraction of earned XP redirected to debt paydown while debt remains. This setting is server synced.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 1f), Array.Empty<object>()));
			MaxDebtPerSkill = config.Bind<int>("General", "MaxDebtPerSkill", 3, new ConfigDescription("Maximum debt per skill, measured in deaths-worth of that skill's current level. 0 = uncapped. This setting is server synced.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 5), Array.Empty<object>()));
			DebugLogging = config.Bind<bool>("General", "DebugLogging", false, "Enable verbose debt conversion/paydown logs. Client-only; not server synced.");
			EnableDebtClearedSound = config.Bind<bool>("General", "EnableDebtClearedSound", true, "Client-side: red-tinted skill halo + crafting-bonus pop sound when a skill's debt is fully repaid. Client-only; not server synced.");
			EnableDevCommands = config.Bind<bool>("Dev", "EnableDevCommands", false, "Server/host only: enables skuld_clearcooldown and skuld_cleardebt on the world host. Not server synced; ignored on multiplayer clients. Still requires Valheim devcommands and admin rights on dedicated servers. Must remain false for Thunderstore releases.");
			DebtBarColor = config.Bind<string>("Visual", "DebtBarColor", "140,31,56", "Client-side color of the debt segment on the gold skill level bar. Use R,G,B (0-255) or #RRGGBB. Client-only; not server synced.");
			DebtTextColor = config.Bind<string>("Visual", "DebtTextColor", "217,46,46", "Client-side color of the -N debt label on skill rows. Use R,G,B (0-255) or #RRGGBB. Client-only; not server synced.");
			DebtTextSize = config.Bind<int>("Visual", "DebtTextSize", 18, new ConfigDescription("Client-side pixel font size for the -N debt label on all skill rows. Client-only; not server synced.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(8, 36), Array.Empty<object>()));
		}

		internal static bool IsDevCommandsEnabled()
		{
			if (!EnableDevCommands.Value)
			{
				return false;
			}
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return true;
			}
			return ZNet.instance.IsServer();
		}

		internal static float GetPaydownShare()
		{
			return Mathf.Clamp(UseServerOverlay ? serverDebtPaydownShare : DebtPaydownShare.Value, 0.5f, 1f);
		}

		internal static int GetMaxDebtPerSkill()
		{
			return Mathf.Clamp(UseServerOverlay ? serverMaxDebtPerSkill : MaxDebtPerSkill.Value, 0, 5);
		}

		internal static Color GetDebtBarColor()
		{
			//IL_000a: 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)
			return ModColorUtil.Parse(DebtBarColor.Value, DefaultDebtBarColor);
		}

		internal static Color GetDebtTextColor()
		{
			//IL_000a: 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)
			return ModColorUtil.Parse(DebtTextColor.Value, DefaultDebtTextColor);
		}

		internal static float GetDebtTextSize()
		{
			return Mathf.Clamp(DebtTextSize.Value, 8, 36);
		}
	}
	internal static class ModConstants
	{
		internal const string ModGuid = "com.cdjensen99.skuld";

		internal const string ModName = "Skuld";

		internal const string ModVersion = "1.0.0";

		internal const string DebtKeyPrefix = "Skuld_Debt_";

		internal const string DebtBaselineKeyPrefix = "Skuld_DebtBaseline_";

		internal const string DebtIncurredKeyPrefix = "Skuld_DebtIncurred_";

		internal const string FocusPaydownAllKey = "Skuld_FocusPaydown_All";

		internal const string FocusPaydownKeyPrefix = "Skuld_FocusPaydown_";

		internal const float FocusPaydownShare = 1f;

		internal const string ConfigSyncRpc = "Skuld_ConfigSync";
	}
	[BepInPlugin("com.cdjensen99.skuld", "Skuld", "1.0.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		private Harmony harmony;

		internal static Plugin Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			ModConfig.Bind(((BaseUnityPlugin)this).Config);
			SkillDebtService.Initialize();
			harmony = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "com.cdjensen99.skuld");
			LogPatchStatus();
			TryRegisterCommands("dev console", DevConsoleCommands.Register);
			TryRegisterCommands("player chat", PlayerChatCommands.Register);
			Log.LogInfo((object)"Skuld 1.0.0 loaded.");
		}

		private static void TryRegisterCommands(string label, Action register)
		{
			try
			{
				register();
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Skuld failed to register {label} commands — patches remain active. {arg}");
			}
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}

		private static void LogPatchStatus()
		{
			LogOriginalPatchStatus(typeof(Skills), "OnDeath");
			LogOriginalPatchStatus(typeof(Skills), "RaiseSkill");
			LogOriginalPatchStatus(typeof(Player), "OnDeath");
			LogOriginalPatchStatus(typeof(SkillsDialog), "Setup");
		}

		private static void LogOriginalPatchStatus(Type type, string methodName)
		{
			MethodInfo methodInfo = AccessTools.Method(type, methodName, (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Log.LogWarning((object)("Skuld patch diagnostic: could not resolve original " + type.FullName + "." + methodName + "."));
				return;
			}
			Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo);
			if (patchInfo == null)
			{
				Log.LogWarning((object)("Skuld patch diagnostic: no Harmony patch info for " + type.FullName + "." + methodName + "."));
			}
			else
			{
				string text = string.Join(", ", patchInfo.Owners.Distinct());
				Log.LogInfo((object)("Skuld patch diagnostic: " + type.FullName + "." + methodName + " patched. Owners=[" + text + "]"));
			}
		}
	}
	internal static class DebtClearedFeedback
	{
		private const float DebounceSeconds = 0.25f;

		private static readonly FieldInfo HeadField = AccessTools.Field(typeof(Character), "m_head");

		private static readonly Color HaloRed = new Color(0.92f, 0.1f, 0.14f, 1f);

		private static readonly Color HaloRedSoft = new Color(0.7f, 0.05f, 0.1f, 1f);

		private static float lastPlayTime = -10f;

		internal static void TryPlay(Player player)
		{
			//IL_0078: 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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: 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)
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			if (!ModConfig.EnableDebtClearedSound.Value)
			{
				Plugin.Log.LogInfo((object)"Skuld debt-cleared feedback skipped: EnableDebtClearedSound=false in config. Set [General] EnableDebtClearedSound=true (close Valheim first, or use Configuration Manager).");
			}
			else if (!(Time.time - lastPlayTime < 0.25f))
			{
				lastPlayTime = Time.time;
				Plugin.Log.LogInfo((object)"Skuld debt-cleared feedback: playing red halo + craft pop.");
				Transform headTransform = GetHeadTransform(player);
				Vector3 position = (((Object)(object)headTransform != (Object)null) ? headTransform.position : ((Character)player).GetHeadPoint());
				Quaternion rotation = (((Object)(object)headTransform != (Object)null) ? headTransform.rotation : ((Component)player).transform.rotation);
				bool num = SpawnRedDebtHalo(player, position, rotation, headTransform);
				bool flag = PlayCraftBonusPop(position);
				if (!num && !flag)
				{
					Plugin.Log.LogWarning((object)"Skuld debt-cleared feedback: both halo and pop failed; trying hard fallbacks.");
					TryHardFallbacks(position);
				}
			}
		}

		private static Transform GetHeadTransform(Player player)
		{
			object? obj = HeadField?.GetValue(player);
			Transform val = (Transform)((obj is Transform) ? obj : null);
			if (val != null && (Object)(object)val != (Object)null)
			{
				return val;
			}
			return ((Component)player).transform;
		}

		private static bool SpawnRedDebtHalo(Player player, Vector3 position, Quaternion rotation, Transform headParent)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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)
			EffectList skillLevelupEffects = player.m_skillLevelupEffects;
			if (skillLevelupEffects != null && skillLevelupEffects.HasEffects())
			{
				GameObject[] array = skillLevelupEffects.Create(position, rotation, headParent, 1f, -1, default(ZDOID));
				int num = 0;
				if (array != null)
				{
					foreach (GameObject val in array)
					{
						if (!((Object)(object)val == (Object)null))
						{
							if (IsAudioOnlyEffect(val))
							{
								Object.Destroy((Object)(object)val);
								continue;
							}
							ApplyRedHaloTint(val);
							num++;
						}
					}
				}
				if (num > 0)
				{
					return true;
				}
			}
			return TrySpawnPrefabVisual("vfx_skilllevelup", position, rotation, headParent);
		}

		private static bool PlayCraftBonusPop(Vector3 position)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			EffectList val = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_craftBonusEffect : null);
			if (val != null && val.HasEffects())
			{
				val.Create(position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
				return true;
			}
			string[] array = new string[6] { "sfx_gui_craftitem", "sfx_gui_craftitem_done", "sfx_gui_craft_item", "sfx_craft_item", "sfx_gui_button", "sfx_gui_repairitem" };
			for (int i = 0; i < array.Length; i++)
			{
				if (TryPlayZsfxPrefab(array[i], position))
				{
					return true;
				}
			}
			return false;
		}

		private static void TryHardFallbacks(Vector3 position)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			TrySpawnPrefabVisual("vfx_HealthUpgrade", position, Quaternion.identity, null);
			TryPlayZsfxPrefab("sfx_levelup", position);
			TryPlayZsfxPrefab("sfx_gui_button", position);
		}

		private static bool TrySpawnPrefabVisual(string prefabName, Vector3 position, Quaternion rotation, Transform parent)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				return false;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(prefabName);
			if ((Object)(object)prefab == (Object)null)
			{
				return false;
			}
			GameObject val = Object.Instantiate<GameObject>(prefab, position, rotation);
			if ((Object)(object)parent != (Object)null)
			{
				val.transform.SetParent(parent, true);
			}
			if (IsAudioOnlyEffect(val))
			{
				ZSFX componentInChildren = val.GetComponentInChildren<ZSFX>(true);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					componentInChildren.m_playOnAwake = false;
					componentInChildren.Play();
					return true;
				}
				Object.Destroy((Object)(object)val);
				return false;
			}
			ApplyRedHaloTint(val);
			return true;
		}

		private static bool IsAudioOnlyEffect(GameObject root)
		{
			if (((Object)root).name.StartsWith("sfx_", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			if ((Object)(object)root.GetComponent<ZSFX>() != (Object)null)
			{
				return (Object)(object)root.GetComponentInChildren<ParticleSystem>(true) == (Object)null;
			}
			return false;
		}

		private static void ApplyRedHaloTint(GameObject root)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			ParticleSystem[] componentsInChildren = root.GetComponentsInChildren<ParticleSystem>(true);
			foreach (ParticleSystem obj in componentsInChildren)
			{
				MainModule main = obj.main;
				((MainModule)(ref main)).startColor = MinMaxGradient.op_Implicit(HaloRed);
				ColorOverLifetimeModule colorOverLifetime = obj.colorOverLifetime;
				((ColorOverLifetimeModule)(ref colorOverLifetime)).enabled = true;
				((ColorOverLifetimeModule)(ref colorOverLifetime)).color = new MinMaxGradient(CreateRedGradient());
			}
			ParticleSystemRenderer[] componentsInChildren2 = root.GetComponentsInChildren<ParticleSystemRenderer>(true);
			for (int j = 0; j < componentsInChildren2.Length; j++)
			{
				if ((Object)(object)((Renderer)componentsInChildren2[j]).material != (Object)null && ((Renderer)componentsInChildren2[j]).material.HasProperty("_Color"))
				{
					((Renderer)componentsInChildren2[j]).material.color = HaloRed;
				}
			}
			TrailRenderer[] componentsInChildren3 = root.GetComponentsInChildren<TrailRenderer>(true);
			for (int k = 0; k < componentsInChildren3.Length; k++)
			{
				componentsInChildren3[k].startColor = HaloRed;
				componentsInChildren3[k].endColor = new Color(HaloRedSoft.r, HaloRedSoft.g, HaloRedSoft.b, 0f);
			}
			Light[] componentsInChildren4 = root.GetComponentsInChildren<Light>(true);
			for (int l = 0; l < componentsInChildren4.Length; l++)
			{
				componentsInChildren4[l].color = HaloRed;
			}
		}

		private static Gradient CreateRedGradient()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			Gradient val = new Gradient();
			val.SetKeys((GradientColorKey[])(object)new GradientColorKey[3]
			{
				new GradientColorKey(HaloRed, 0f),
				new GradientColorKey(HaloRedSoft, 0.55f),
				new GradientColorKey(new Color(0.45f, 0.02f, 0.05f), 1f)
			}, (GradientAlphaKey[])(object)new GradientAlphaKey[3]
			{
				new GradientAlphaKey(1f, 0f),
				new GradientAlphaKey(0.85f, 0.65f),
				new GradientAlphaKey(0f, 1f)
			});
			return val;
		}

		private static bool TryPlayZsfxPrefab(string prefabName, Vector3 position)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				return false;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(prefabName);
			if ((Object)(object)prefab == (Object)null)
			{
				return false;
			}
			GameObject val = Object.Instantiate<GameObject>(prefab, position, Quaternion.identity);
			ZSFX componentInChildren = val.GetComponentInChildren<ZSFX>(true);
			if ((Object)(object)componentInChildren == (Object)null)
			{
				AudioSource componentInChildren2 = val.GetComponentInChildren<AudioSource>(true);
				if ((Object)(object)componentInChildren2 != (Object)null && (Object)(object)componentInChildren2.clip != (Object)null)
				{
					componentInChildren2.Play();
					Object.Destroy((Object)(object)val, componentInChildren2.clip.length + 0.25f);
					return true;
				}
				Object.Destroy((Object)(object)val);
				return false;
			}
			componentInChildren.m_playOnAwake = false;
			componentInChildren.Play();
			return true;
		}
	}
	internal sealed class DebtTooltipBinder : MonoBehaviour
	{
		private SkillType skillType;

		private Skill skill;

		private UITooltip tooltip;

		private RectTransform tooltipAnchor;

		private Vector2 fixedPosition;

		private string baseDescription = string.Empty;

		internal void Initialize(SkillType type, Skill skillRef, UITooltip tip, string description, RectTransform anchor, Vector2 position)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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)
			skillType = type;
			skill = skillRef;
			tooltip = tip;
			baseDescription = description ?? string.Empty;
			tooltipAnchor = anchor;
			fixedPosition = position;
			Refresh();
		}

		private void LateUpdate()
		{
			Refresh();
		}

		private void Refresh()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)tooltip == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				if (skill != null)
				{
					SkillDebtBarOverlay.Apply(((Component)this).transform, Player.m_localPlayer, skill);
				}
				if (SkillDebtService.GetDebt(Player.m_localPlayer, skillType) <= 0f)
				{
					tooltip.Set(string.Empty, baseDescription, tooltipAnchor, fixedPosition);
					return;
				}
				string text = SkillDebtService.FormatProgressDetail(Player.m_localPlayer, skillType);
				string text2 = (string.IsNullOrEmpty(baseDescription) ? text : (baseDescription + "\n\n" + text));
				tooltip.Set(string.Empty, text2, tooltipAnchor, fixedPosition);
			}
		}
	}
	internal static class DevConsoleCommands
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__ClearCooldown;

			public static ConsoleEvent <1>__ClearDebt;
		}

		private static bool registered;

		internal static void Register()
		{
			//IL_0046: 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_003c: Expected O, but got Unknown
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			if (registered)
			{
				Plugin.Log.LogWarning((object)"Skuld dev console commands already registered; skipping duplicate registration.");
				return;
			}
			object obj = <>O.<0>__ClearCooldown;
			if (obj == null)
			{
				ConsoleEvent val = ClearCooldown;
				<>O.<0>__ClearCooldown = val;
				obj = (object)val;
			}
			new ConsoleCommand("skuld_clearcooldown", "Skuld dev: force the next death to count as a hard death.", (ConsoleEvent)obj, true, false, true, true, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			object obj2 = <>O.<1>__ClearDebt;
			if (obj2 == null)
			{
				ConsoleEvent val2 = ClearDebt;
				<>O.<1>__ClearDebt = val2;
				obj2 = (object)val2;
			}
			new ConsoleCommand("skuld_cleardebt", "Skuld dev: clear all outstanding skill debt for the local player.", (ConsoleEvent)obj2, true, false, true, true, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			registered = true;
			string text = (ModConfig.IsDevCommandsEnabled() ? "enabled on server/host" : "disabled (set EnableDevCommands=true on the server/host cfg)");
			Plugin.Log.LogInfo((object)("Skuld dev console commands registered (" + text + "): skuld_clearcooldown, skuld_cleardebt."));
		}

		private static void ClearCooldown(ConsoleEventArgs args)
		{
			if (!ModConfig.IsDevCommandsEnabled())
			{
				args.Context.AddString("Skuld dev commands are disabled. Set EnableDevCommands=true in the server/host BepInEx config.");
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				args.Context.AddString("No local player.");
				return;
			}
			localPlayer.ClearHardDeath();
			args.Context.AddString("Skuld: hard-death cooldown cleared. Next death will generate debt.");
		}

		private static void ClearDebt(ConsoleEventArgs args)
		{
			if (!ModConfig.IsDevCommandsEnabled())
			{
				args.Context.AddString("Skuld dev commands are disabled. Set EnableDevCommands=true in the server/host BepInEx config.");
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				args.Context.AddString("No local player.");
				return;
			}
			List<string> list = SkillDebtService.ClearAllDebt(localPlayer);
			if (list.Count == 0)
			{
				args.Context.AddString("Skuld: no debt to clear.");
			}
			else
			{
				args.Context.AddString("Skuld: cleared debt for " + string.Join(", ", list) + ".");
			}
		}
	}
	internal static class PlayerChatCommands
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__ShowCommand;

			public static ConsoleOptionsFetcher <1>__GetShowTabOptions;

			public static ConsoleEvent <2>__FocusCommand;

			public static ConsoleOptionsFetcher <3>__GetFocusTabOptions;
		}

		private static bool registered;

		internal static void Register()
		{
			//IL_0022: 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: Expected O, but got Unknown
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//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_007c: Expected O, but got Unknown
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			if (!registered)
			{
				object obj = <>O.<0>__ShowCommand;
				if (obj == null)
				{
					ConsoleEvent val = ShowCommand;
					<>O.<0>__ShowCommand = val;
					obj = (object)val;
				}
				object obj2 = <>O.<1>__GetShowTabOptions;
				if (obj2 == null)
				{
					ConsoleOptionsFetcher val2 = GetShowTabOptions;
					<>O.<1>__GetShowTabOptions = val2;
					obj2 = (object)val2;
				}
				new ConsoleCommand("show", "Skuld: /show debt [...] or /show focus — show debt or focus paydown status.", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)obj2, false, false, false);
				object obj3 = <>O.<2>__FocusCommand;
				if (obj3 == null)
				{
					ConsoleEvent val3 = FocusCommand;
					<>O.<2>__FocusCommand = val3;
					obj3 = (object)val3;
				}
				object obj4 = <>O.<3>__GetFocusTabOptions;
				if (obj4 == null)
				{
					ConsoleOptionsFetcher val4 = GetFocusTabOptions;
					<>O.<3>__GetFocusTabOptions = val4;
					obj4 = (object)val4;
				}
				new ConsoleCommand("focus", "Skuld: /focus <skill|all|off> — 100% debt paydown until cleared.", (ConsoleEvent)obj3, false, false, false, false, false, false, (ConsoleOptionsFetcher)obj4, false, false, false);
				registered = true;
				Plugin.Log.LogInfo((object)"Skuld player chat commands registered: /show debt, /show focus, /focus");
			}
		}

		private static void ShowCommand(ConsoleEventArgs args)
		{
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			if (args.Length < 2)
			{
				args.Context.AddString("Usage: /show debt [...] or /show focus");
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				args.Context.AddString("No local player.");
				return;
			}
			if (string.Equals(args[1], "focus", StringComparison.OrdinalIgnoreCase))
			{
				SkillDebtService.ShowFocusOverview(localPlayer, args.Context);
				return;
			}
			if (!string.Equals(args[1], "debt", StringComparison.OrdinalIgnoreCase))
			{
				args.Context.AddString("Usage: /show debt [...] or /show focus");
				return;
			}
			if (args.Length < 3)
			{
				args.Context.AddString("Usage: /show debt [skill|all|lifetime <skill|all>]");
				return;
			}
			string[] array = args.Args.Skip(2).ToArray();
			if (array.Length >= 1 && string.Equals(array[0], "lifetime", StringComparison.OrdinalIgnoreCase))
			{
				HandleLifetimeCommand(localPlayer, args.Context, array.Skip(1).ToArray());
				return;
			}
			string text = string.Join(string.Empty, array);
			if (string.Equals(text, "all", StringComparison.OrdinalIgnoreCase) || string.Equals(string.Join(" ", array), "all", StringComparison.OrdinalIgnoreCase))
			{
				SkillDebtService.ShowAllDebtOverview(localPlayer, args.Context);
				return;
			}
			if (!SkillDebtService.TryResolveSkillType(text, out var skillType))
			{
				text = string.Join(" ", array);
				if (!SkillDebtService.TryResolveSkillType(text, out skillType))
				{
					args.Context.AddString("Unknown skill '" + text + "'.");
					return;
				}
			}
			float debt = SkillDebtService.GetDebt(localPlayer, skillType);
			float paidOff = SkillDebtService.GetPaidOff(localPlayer, skillType);
			if (debt <= 0f && paidOff <= 0f)
			{
				args.Context.AddString(SkillDebtService.FormatSkillName(skillType) + ": no debt recorded.");
				return;
			}
			string text2 = SkillDebtService.FormatProgressDetail(localPlayer, skillType);
			args.Context.AddString(SkillDebtService.FormatSkillName(skillType) + ": " + text2);
			MessageHud instance = MessageHud.instance;
			if (instance != null)
			{
				instance.ShowMessage((MessageType)1, SkillDebtService.FormatSkillName(skillType) + " " + text2, 0, (Sprite)null, false, true);
			}
		}

		private static void FocusCommand(ConsoleEventArgs args)
		{
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			if (!ModConfig.IsModEnabled)
			{
				args.Context.AddString("Skuld is disabled.");
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				args.Context.AddString("No local player.");
				return;
			}
			if (args.Length < 2)
			{
				args.Context.AddString("Usage: /focus <skill|all|off>");
				return;
			}
			string text = string.Join(" ", args.Args.Skip(1));
			SkillType skillType;
			string message2;
			if (string.Equals(text, "off", StringComparison.OrdinalIgnoreCase))
			{
				SkillDebtService.ClearAllFocusPaydown(localPlayer);
				args.Context.AddString("Focused paydown cleared.");
				MessageHud instance = MessageHud.instance;
				if (instance != null)
				{
					instance.ShowMessage((MessageType)1, "Focused paydown cleared.", 0, (Sprite)null, false, true);
				}
			}
			else if (string.Equals(text, "all", StringComparison.OrdinalIgnoreCase))
			{
				if (!SkillDebtService.TryEnableFocusAll(localPlayer, out var message))
				{
					args.Context.AddString("Could not enable focus all.");
					return;
				}
				args.Context.AddString(message);
				MessageHud instance2 = MessageHud.instance;
				if (instance2 != null)
				{
					instance2.ShowMessage((MessageType)1, message, 0, (Sprite)null, false, true);
				}
			}
			else if (!SkillDebtService.TryResolveSkillType(text, out skillType))
			{
				args.Context.AddString("Unknown skill '" + text + "'.");
			}
			else if (!SkillDebtService.TryEnableFocusSkill(localPlayer, skillType, out message2))
			{
				args.Context.AddString("Could not enable focused paydown.");
			}
			else
			{
				args.Context.AddString(message2);
				MessageHud instance3 = MessageHud.instance;
				if (instance3 != null)
				{
					instance3.ShowMessage((MessageType)1, message2, 0, (Sprite)null, false, true);
				}
			}
		}

		private static void HandleLifetimeCommand(Player player, Terminal context, string[] tail)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			if (tail.Length == 0)
			{
				context.AddString("Usage: /show debt lifetime <skill|all>");
				return;
			}
			string text = string.Join(" ", tail);
			if (string.Equals(text, "all", StringComparison.OrdinalIgnoreCase))
			{
				SkillDebtService.ShowAllLifetimeOverview(player, context);
				return;
			}
			if (!SkillDebtService.TryResolveSkillType(text, out var skillType))
			{
				context.AddString("Unknown skill '" + text + "'.");
				return;
			}
			float incurred = SkillDebtService.GetIncurred(player, skillType);
			float lifetimeRepaid = SkillDebtService.GetLifetimeRepaid(player, skillType);
			if (incurred <= 0f && lifetimeRepaid <= 0f)
			{
				context.AddString(SkillDebtService.FormatSkillName(skillType) + ": no lifetime debt recorded.");
				return;
			}
			string text2 = SkillDebtService.FormatLifetimeDetail(player, skillType);
			context.AddString(SkillDebtService.FormatSkillName(skillType) + ": " + text2);
			MessageHud instance = MessageHud.instance;
			if (instance != null)
			{
				instance.ShowMessage((MessageType)1, SkillDebtService.FormatSkillName(skillType) + ": " + text2, 0, (Sprite)null, false, true);
			}
		}

		private static List<string> GetShowTabOptions()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string> { "debt", "focus", "all", "lifetime" };
			foreach (SkillType value in Enum.GetValues(typeof(SkillType)))
			{
				if ((int)value != 0)
				{
					list.Add(((object)value/*cast due to .constrained prefix*/).ToString().ToLowerInvariant());
				}
			}
			return list;
		}

		private static List<string> GetFocusTabOptions()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string> { "all", "off" };
			foreach (SkillType value in Enum.GetValues(typeof(SkillType)))
			{
				if ((int)value != 0)
				{
					list.Add(((object)value/*cast due to .constrained prefix*/).ToString().ToLowerInvariant());
				}
			}
			return list;
		}
	}
	internal static class ServerConfigSync
	{
		private const byte PackageVersion = 1;

		private static bool rpcRegistered;

		internal static void Initialize()
		{
			if (!rpcRegistered && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.Register<ZPackage>("Skuld_ConfigSync", (Action<long, ZPackage>)ReceiveFromServer);
				rpcRegistered = true;
				Plugin.Log.LogInfo((object)"Skuld server config sync RPC registered.");
			}
		}

		internal static void SendToPeer(ZNetPeer peer)
		{
			if (peer != null && ZRoutedRpc.instance != null && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				ZPackage val = WriteServerPackage();
				ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_uid, "Skuld_ConfigSync", new object[1] { val });
				Plugin.Log.LogInfo((object)($"Skuld sent gameplay config to peer {peer.m_playerName} (uid={peer.m_uid}): " + $"EnableMod={ModConfig.EnableMod.Value}, Paydown={ModConfig.GetPaydownShare():F2}, MaxDebt={ModConfig.GetMaxDebtPerSkill()}"));
			}
		}

		internal static void ClearSession()
		{
			ModConfig.ClearServerOverlay();
		}

		private static void ReceiveFromServer(long sender, ZPackage package)
		{
			if (package == null || (Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer())
			{
				return;
			}
			if (!TryReadServerPackage(package, out var enableMod, out var paydownShare, out var maxDebtPerSkill))
			{
				Plugin.Log.LogWarning((object)"Skuld server config sync: ignored malformed package.");
				return;
			}
			bool value = ModConfig.EnableMod.Value;
			ModConfig.ApplyServerOverlay(enableMod, paydownShare, maxDebtPerSkill);
			Plugin.Log.LogInfo((object)$"Skuld applied server gameplay config: EnableMod={enableMod}, Paydown={paydownShare:F2}, MaxDebt={maxDebtPerSkill}");
			if ((Object)(object)Plugin.Instance != (Object)null)
			{
				((MonoBehaviour)Plugin.Instance).StartCoroutine(ShowJoinMessagesAfterPlayerReady(value, enableMod, paydownShare, maxDebtPerSkill));
			}
		}

		private static ZPackage WriteServerPackage()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write((byte)1);
			val.Write(ModConfig.EnableMod.Value);
			val.Write(ModConfig.GetPaydownShare());
			val.Write(ModConfig.GetMaxDebtPerSkill());
			return val;
		}

		private static bool TryReadServerPackage(ZPackage package, out bool enableMod, out float paydownShare, out int maxDebtPerSkill)
		{
			enableMod = true;
			paydownShare = 0.5f;
			maxDebtPerSkill = 3;
			if (package.Size() < 13)
			{
				return false;
			}
			if (package.ReadByte() != 1)
			{
				return false;
			}
			enableMod = package.ReadBool();
			paydownShare = package.ReadSingle();
			maxDebtPerSkill = package.ReadInt();
			return true;
		}

		private static IEnumerator ShowJoinMessagesAfterPlayerReady(bool localEnableMod, bool serverEnableMod, float paydownShare, int maxDebtPerSkill)
		{
			for (int i = 0; i < 600; i++)
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null)
				{
					if (localEnableMod != serverEnableMod)
					{
						string text = (serverEnableMod ? "Skuld: server has debt enabled but your local EnableMod is false. Gameplay uses server rules; keep Skuld installed for UI." : "Skuld: server has debt disabled. Your local EnableMod setting is ignored while connected.");
						((Character)localPlayer).Message((MessageType)1, text, 0, (Sprite)null, false);
					}
					if (serverEnableMod)
					{
						string arg = ((maxDebtPerSkill <= 0) ? "uncapped" : $"{maxDebtPerSkill}x death debt");
						int num = Mathf.RoundToInt(paydownShare * 100f);
						((Character)localPlayer).Message((MessageType)1, $"Skuld server rules: {num}% paydown, max {arg}", 0, (Sprite)null, false);
					}
					break;
				}
				yield return null;
			}
		}

		internal static ZNetPeer FindPeerByRpc(ZRpc rpc)
		{
			if ((Object)(object)ZNet.instance == (Object)null || rpc == null)
			{
				return null;
			}
			List<ZNetPeer> peers = ZNet.instance.GetPeers();
			for (int i = 0; i < peers.Count; i++)
			{
				ZNetPeer val = peers[i];
				if (val?.m_rpc == rpc)
				{
					return val;
				}
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(ZNet), "Start")]
	internal static class ZNetStartServerConfigSyncPatch
	{
		private static void Postfix()
		{
			ServerConfigSync.Initialize();
		}
	}
	[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
	internal static class ZNetPeerInfoServerConfigSyncPatch
	{
		private static void Postfix(ZNet __instance, ZRpc rpc)
		{
			if (!((Object)(object)__instance == (Object)null) && __instance.IsServer())
			{
				ServerConfigSync.Initialize();
				ZNetPeer val = ServerConfigSync.FindPeerByRpc(rpc);
				if (val != null)
				{
					ServerConfigSync.SendToPeer(val);
				}
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "Shutdown")]
	internal static class ZNetShutdownServerConfigSyncPatch
	{
		private static void Prefix()
		{
			ServerConfigSync.ClearSession();
		}
	}
	internal static class SkillDebtBarOverlay
	{
		private const string DebtBarName = "skuld_debtbar";

		private const string DebtTextName = "skuld_debttext";

		private const float LevelBarScale = 100f;

		private const float LabelGapPixels = 4f;

		internal static void Apply(Transform row, Player player, Skill skill)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)row == (Object)null || (Object)(object)player == (Object)null || skill?.m_info == null)
			{
				return;
			}
			RemoveLegacyDebtBar(Utils.FindChild(row, "currentlevel", (IterativeSearchType)0));
			Transform val = Utils.FindChild(row, "levelbar", (IterativeSearchType)0);
			if (!((Object)(object)val == (Object)null))
			{
				GuiBar component = ((Component)val).GetComponent<GuiBar>();
				if (!((Object)(object)component == (Object)null))
				{
					float debt = SkillDebtService.GetDebt(player, skill.m_info.m_skill);
					float levelBarFill = GetLevelBarFill(component);
					float barDenominator = ResolveBarDenominator(skill.m_info.m_skill, skill.m_level, levelBarFill);
					ComputeDebtBarSegment(skill.m_level, debt, barDenominator, levelBarFill, out var segmentStart, out var segmentEnd);
					float num = segmentEnd - segmentStart;
					bool showDebt = debt > 0f && num > 0f;
					ApplyMaroonSegment(val, component, segmentStart, segmentEnd, showDebt);
					ApplyDebtLabel(row, segmentStart, segmentEnd, debt, showDebt);
					EnsureVanillaLabelsDrawAboveDebtBar(row);
				}
			}
		}

		private static void EnsureVanillaLabelsDrawAboveDebtBar(Transform row)
		{
			Transform val = Utils.FindChild(row, "leveltext", (IterativeSearchType)0);
			if ((Object)(object)val != (Object)null)
			{
				val.SetAsLastSibling();
			}
			Transform val2 = Utils.FindChild(row, "bonustext", (IterativeSearchType)0);
			if ((Object)(object)val2 != (Object)null)
			{
				val2.SetAsLastSibling();
			}
		}

		internal static void ComputeDebtBarSegment(float skillLevel, float debtLevels, float barDenominator, float levelBarFill, out float segmentStart, out float segmentEnd)
		{
			segmentStart = 0f;
			segmentEnd = 0f;
			if (!(debtLevels <= 0f) && !(skillLevel <= 0f))
			{
				float num = ((barDenominator > 0f) ? barDenominator : 100f);
				float num2 = ((levelBarFill > 0f) ? Mathf.Clamp01(levelBarFill) : Mathf.Clamp01(skillLevel / num));
				float num3 = Mathf.Min(debtLevels / num, num2);
				segmentEnd = num2;
				segmentStart = num2 - num3;
			}
		}

		private static float GetLevelBarFill(GuiBar levelBar)
		{
			if (!((Object)(object)levelBar != (Object)null))
			{
				return 0f;
			}
			return Mathf.Clamp01(levelBar.GetSmoothValue());
		}

		private static float ResolveBarDenominator(SkillType skillType, float skillLevel, float levelBarFill)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			if (SkillLimitExtenderCompat.TryGetUiDenominator(skillType, out var denominator))
			{
				return denominator;
			}
			if (skillLevel > 0f && levelBarFill > 0.0001f)
			{
				return skillLevel / levelBarFill;
			}
			return 100f;
		}

		private static void ApplyMaroonSegment(Transform levelBarTransform, GuiBar levelBar, float segmentStart, float segmentEnd, bool showDebt)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			Image orCreateDebtImage = GetOrCreateDebtImage(levelBarTransform, levelBar);
			if (!((Object)(object)orCreateDebtImage == (Object)null))
			{
				((Component)orCreateDebtImage).gameObject.SetActive(showDebt);
				if (showDebt)
				{
					((Graphic)orCreateDebtImage).color = ModConfig.GetDebtBarColor();
					RectTransform rectTransform = ((Graphic)orCreateDebtImage).rectTransform;
					rectTransform.anchorMin = new Vector2(segmentStart, 0f);
					rectTransform.anchorMax = new Vector2(segmentEnd, 1f);
					rectTransform.offsetMin = Vector2.zero;
					rectTransform.offsetMax = Vector2.zero;
					((Transform)rectTransform).SetAsLastSibling();
				}
			}
		}

		private static void ApplyDebtLabel(Transform row, float maroonStart, float maroonEnd, float debt, bool showDebt)
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: 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_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			Transform val = Utils.FindChild(row, "levelbar", (IterativeSearchType)0);
			TMP_Text orCreateDebtText = GetOrCreateDebtText(row, val);
			if ((Object)(object)orCreateDebtText == (Object)null)
			{
				return;
			}
			((Component)orCreateDebtText).gameObject.SetActive(showDebt);
			if (showDebt)
			{
				orCreateDebtText.text = (-Mathf.RoundToInt(debt)).ToString("0");
				ApplyDebtTextStyle(orCreateDebtText, row);
				orCreateDebtText.ForceMeshUpdate(false, false);
				RectTransform rectTransform = orCreateDebtText.rectTransform;
				RectTransform val2 = (RectTransform)(object)((val is RectTransform) ? val : null);
				if (val2 != null)
				{
					PlaceDebtLabelAtBarStart(orCreateDebtText, rectTransform, val2);
					((Transform)rectTransform).SetAsLastSibling();
					return;
				}
				((Transform)rectTransform).SetParent(val, false);
				rectTransform.anchorMin = new Vector2(maroonStart, 0f);
				rectTransform.anchorMax = new Vector2(maroonEnd, 1f);
				rectTransform.offsetMin = Vector2.zero;
				rectTransform.offsetMax = Vector2.zero;
				rectTransform.pivot = new Vector2(0.5f, 0.5f);
				rectTransform.anchoredPosition = Vector2.zero;
				rectTransform.sizeDelta = Vector2.zero;
				orCreateDebtText.alignment = (TextAlignmentOptions)514;
				((Transform)rectTransform).SetAsLastSibling();
			}
		}

		private static void PlaceDebtLabelAtBarStart(TMP_Text debtText, RectTransform debtRect, RectTransform levelBarRect)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			Transform parent = ((Transform)levelBarRect).parent;
			((Transform)debtRect).SetParent(parent, false);
			float num = Mathf.Max(28f, debtText.preferredWidth + 4f);
			Rect rect = levelBarRect.rect;
			float num2;
			if (!(((Rect)(ref rect)).height > 0f))
			{
				num2 = 28f;
			}
			else
			{
				rect = levelBarRect.rect;
				num2 = ((Rect)(ref rect)).height;
			}
			float num3 = num2;
			rect = levelBarRect.rect;
			float num4 = ((Rect)(ref rect)).xMin + 4f;
			rect = levelBarRect.rect;
			Vector3 val = ((Transform)levelBarRect).TransformPoint(new Vector3(num4, ((Rect)(ref rect)).center.y, 0f));
			Vector3 localPosition = parent.InverseTransformPoint(val);
			debtRect.anchorMin = new Vector2(0.5f, 0.5f);
			debtRect.anchorMax = new Vector2(0.5f, 0.5f);
			debtRect.pivot = new Vector2(0f, 0.5f);
			debtRect.sizeDelta = new Vector2(num, num3);
			((Transform)debtRect).localPosition = localPosition;
			debtText.alignment = (TextAlignmentOptions)4097;
		}

		private static void RemoveLegacyDebtBar(Transform progressBarTransform)
		{
			if (!((Object)(object)progressBarTransform == (Object)null))
			{
				Transform val = progressBarTransform.Find("skuld_debtbar");
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)val).gameObject);
				}
			}
		}

		private static Image GetOrCreateDebtImage(Transform trackTransform, GuiBar levelBar)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: 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)
			Transform val = trackTransform.Find("skuld_debtbar");
			if ((Object)(object)val != (Object)null)
			{
				return ((Component)val).GetComponent<Image>();
			}
			Image val2 = (((Object)(object)levelBar.m_bar != (Object)null) ? ((Component)levelBar.m_bar).GetComponent<Image>() : null);
			GameObject val3 = new GameObject("skuld_debtbar", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			val3.transform.SetParent(trackTransform, false);
			Image component = val3.GetComponent<Image>();
			((Graphic)component).raycastTarget = false;
			((Graphic)component).color = ModConfig.GetDebtBarColor();
			if ((Object)(object)val2 != (Object)null)
			{
				component.sprite = val2.sprite;
				component.type = val2.type;
				((Graphic)component).material = ((Graphic)val2).material;
				component.pixelsPerUnitMultiplier = val2.pixelsPerUnitMultiplier;
			}
			return component;
		}

		private static TMP_Text GetOrCreateDebtText(Transform row, Transform levelBarTransform)
		{
			Transform val = Utils.FindChild(row, "skuld_debttext", (IterativeSearchType)0);
			if ((Object)(object)val != (Object)null && (Object)(object)val.parent == (Object)(object)levelBarTransform)
			{
				Object.Destroy((Object)(object)((Component)val).gameObject);
				val = null;
			}
			if ((Object)(object)val != (Object)null)
			{
				TMP_Text component = ((Component)val).GetComponent<TMP_Text>();
				ApplyDebtTextStyle(component, row);
				return component;
			}
			TMP_Text debtTextFontSource = GetDebtTextFontSource(row);
			if ((Object)(object)debtTextFontSource == (Object)null || (Object)(object)levelBarTransform == (Object)null)
			{
				return null;
			}
			GameObject val2 = Object.Instantiate<GameObject>(((Component)debtTextFontSource).gameObject, debtTextFontSource.transform.parent);
			((Object)val2).name = "skuld_debttext";
			val2.SetActive(false);
			StripNonTextComponents(val2);
			TMP_Text component2 = val2.GetComponent<TMP_Text>();
			((Graphic)component2).raycastTarget = false;
			component2.text = string.Empty;
			ApplyDebtTextStyle(component2, row);
			if ((Object)(object)val2.GetComponent<CanvasGroup>() == (Object)null)
			{
				CanvasGroup obj = val2.AddComponent<CanvasGroup>();
				obj.blocksRaycasts = false;
				obj.interactable = false;
			}
			return component2;
		}

		private static TMP_Text GetDebtTextFontSource(Transform row)
		{
			Transform obj = Utils.FindChild(row, "leveltext", (IterativeSearchType)0);
			object obj2 = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null);
			if (obj2 == null)
			{
				Transform obj3 = Utils.FindChild(row, "bonustext", (IterativeSearchType)0);
				if (obj3 == null)
				{
					return null;
				}
				obj2 = ((Component)obj3).GetComponent<TMP_Text>();
			}
			return (TMP_Text)obj2;
		}

		private static void ApplyDebtTextStyle(TMP_Text debtText, Transform row)
		{
			//IL_0034: 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_0089: Unknown result type (might be due to invalid IL or missing references)
			TMP_Text debtTextFontSource = GetDebtTextFontSource(row);
			if (!((Object)(object)debtText == (Object)null) && !((Object)(object)debtTextFontSource == (Object)null))
			{
				debtText.font = debtTextFontSource.font;
				debtText.fontSharedMaterial = debtTextFontSource.fontSharedMaterial;
				debtText.fontStyle = debtTextFontSource.fontStyle;
				debtText.characterSpacing = debtTextFontSource.characterSpacing;
				debtText.wordSpacing = debtTextFontSource.wordSpacing;
				debtText.lineSpacing = debtTextFontSource.lineSpacing;
				debtText.paragraphSpacing = debtTextFontSource.paragraphSpacing;
				debtText.textWrappingMode = debtTextFontSource.textWrappingMode;
				debtText.overflowMode = (TextOverflowModes)0;
				debtText.enableAutoSizing = false;
				((Graphic)debtText).color = ModConfig.GetDebtTextColor();
				debtText.fontSize = ModConfig.GetDebtTextSize();
			}
		}

		private static void StripNonTextComponents(GameObject debtObject)
		{
			Component[] components = debtObject.GetComponents<Component>();
			foreach (Component val in components)
			{
				if (!(val is Transform) && !(val is RectTransform) && !(val is TextMeshProUGUI) && !(val is CanvasRenderer))
				{
					Object.Destroy((Object)(object)val);
				}
			}
		}
	}
	internal static class SkillDebtService
	{
		private static readonly FieldInfo SkillsDataField = AccessTools.Field(typeof(Skills), "m_skillData");

		private static readonly FieldInfo DeathLowerFactorField = AccessTools.Field(typeof(Skills), "m_DeathLowerFactor");

		private static readonly FieldInfo SkillsPlayerField = AccessTools.Field(typeof(Skills), "m_player");

		private static readonly Type SkillType = AccessTools.Inner(typeof(Skills), "Skill");

		private static readonly FieldInfo SkillLevelField = ((SkillType != null) ? AccessTools.Field(SkillType, "m_level") : null);

		private static readonly FieldInfo SkillInfoField = ((SkillType != null) ? AccessTools.Field(SkillType, "m_info") : null);

		private static readonly Type SkillDefType = AccessTools.Inner(typeof(Skills), "SkillDef");

		private static readonly FieldInfo SkillIncreaseStepField = ((SkillDefType != null) ? AccessTools.Field(SkillDefType, "m_increseStep") : null);

		internal static bool IsReady
		{
			get
			{
				if (SkillsDataField != null && DeathLowerFactorField != null && SkillsPlayerField != null && SkillLevelField != null && SkillInfoField != null)
				{
					return SkillIncreaseStepField != null;
				}
				return false;
			}
		}

		internal static void Initialize()
		{
			if (SkillsDataField == null || DeathLowerFactorField == null || SkillsPlayerField == null || SkillLevelField == null || SkillInfoField == null || SkillIncreaseStepField == null)
			{
				Plugin.Log.LogError((object)"Skuld failed to bind reflection fields; debt features may not work.");
			}
			Plugin.Log.LogInfo((object)"Skuld debt storage: Player.m_customData (character save). Not written to player ZDO — other players cannot read your debt keys.");
		}

		internal static void LogHardDeathDiagnostics(Skills skills)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			if (!IsReady)
			{
				Plugin.Log.LogInfo((object)"Skuld hard-death diagnostic: reflection bindings not ready.");
				return;
			}
			float num = (float)DeathLowerFactorField.GetValue(skills);
			float skillReductionRate = Game.m_skillReductionRate;
			float num2 = num * skillReductionRate;
			Plugin.Log.LogInfo((object)$"Skuld hard-death diagnostic: m_DeathLowerFactor={num:F6}, Game.m_skillReductionRate={skillReductionRate:F6}, factor={num2:F6}");
			if (!(SkillsDataField.GetValue(skills) is IDictionary dictionary))
			{
				Plugin.Log.LogInfo((object)"Skuld hard-death diagnostic: m_skillData is null.");
				return;
			}
			foreach (DictionaryEntry item in dictionary)
			{
				try
				{
					SkillType val = (SkillType)item.Key;
					if (item.Value != null)
					{
						float skillLevel = GetSkillLevel(item.Value);
						float num3 = skillLevel * num2;
						Plugin.Log.LogInfo((object)$"Skuld hard-death diagnostic: skill={val}, level={skillLevel:F3}, lossAmount={num3:F6}");
					}
				}
				catch (Exception arg)
				{
					Plugin.Log.LogError((object)$"Skuld hard-death diagnostic failed for skill entry {item.Key}: {arg}");
				}
			}
		}

		internal static bool TryConvertDeathToDebt(Skills skills, out string deathSummary)
		{
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			deathSummary = string.Empty;
			object? obj = SkillsPlayerField?.GetValue(skills);
			Player val = (Player)((obj is Player) ? obj : null);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)"Skuld death conversion: Skills.m_player was null.");
				return false;
			}
			float deathFactor = GetDeathFactor(skills);
			if (deathFactor <= 0f)
			{
				return true;
			}
			if (!(SkillsDataField?.GetValue(skills) is IDictionary dictionary))
			{
				return false;
			}
			int maxDebtPerSkill = ModConfig.GetMaxDebtPerSkill();
			List<string> list = new List<string>();
			int num = 0;
			Plugin.Log.LogInfo((object)$"Skuld death conversion: starting per-skill customData writes. player={DescribePlayer(val)}, MaxDebtPerSkill={maxDebtPerSkill}");
			foreach (DictionaryEntry item in dictionary)
			{
				try
				{
					SkillType val2 = (SkillType)item.Key;
					if (item.Value == null)
					{
						continue;
					}
					float skillLevel = GetSkillLevel(item.Value);
					if (skillLevel <= 0f)
					{
						continue;
					}
					float num2 = skillLevel * deathFactor;
					if (!(num2 <= 0f))
					{
						EnsureProgressConsistency(val, val2);
						float num3 = GetDebt(val, val2) + num2;
						float num4 = GetIncurred(val, val2) + num2;
						if (maxDebtPerSkill > 0)
						{
							float num5 = (float)maxDebtPerSkill * num2;
							num3 = Mathf.Min(num3, num5);
						}
						SetDebt(val, val2, num3);
						SetIncurred(val, val2, num4);
						SetBaseline(val, val2, num3);
						float debt = GetDebt(val, val2);
						num++;
						list.Add($"{FormatSkillName(val2)} +{num2:0.0}");
						Plugin.Log.LogInfo((object)$"Skuld death conversion: skill={val2}, raw={num2:F4}, current={num3:F4}, baseline={num3:F4}, lifetime={num4:F4}, customDataReadback={debt:F4}, key={GetDebtKey(val2)}");
						LogDebug($"Death debt added: {val2} raw +{num2:F3} (current {num3:F3}, lifetime {num4:F3})");
					}
				}
				catch (Exception arg)
				{
					Plugin.Log.LogError((object)$"Skuld death conversion failed for skill entry {item.Key}: {arg}");
				}
			}
			Plugin.Log.LogInfo((object)$"Skuld death conversion: complete, {num} skills debted.");
			if (list.Count > 0)
			{
				deathSummary = BuildDeathSummary(list);
			}
			return true;
		}

		internal static bool TryApplyRaiseSkillDebt(Skills skills, SkillType skillType, ref float value)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			object? obj = SkillsPlayerField?.GetValue(skills);
			Player val = (Player)((obj is Player) ? obj : null);
			if (value <= 0f || (int)skillType == 0 || (Object)(object)val == (Object)null)
			{
				return true;
			}
			EnsureProgressConsistency(val, skillType);
			float debt = GetDebt(val, skillType);
			if (debt <= 0f)
			{
				return true;
			}
			Skill skillFromMap = GetSkillFromMap(skills, skillType);
			if (skillFromMap == null)
			{
				return true;
			}
			float increaseStep = GetIncreaseStep(skillFromMap);
			if (increaseStep <= 0f)
			{
				return true;
			}
			float num = increaseStep * value * Game.m_skillGainRate;
			float paydownShare = GetPaydownShare(val, skillType);
			float num2 = 1f - paydownShare;
			float nextLevelRequirement = GetNextLevelRequirement(skillFromMap.m_level);
			float num3 = num * paydownShare;
			float num4 = num * num2;
			float num5 = ((nextLevelRequirement > 0f) ? (num3 / nextLevelRequirement) : 0f);
			float num6 = Mathf.Min(debt, num5);
			float num7 = num3 - num6 * nextLevelRequirement;
			float num8 = num4 + num7;
			float num9 = increaseStep * Game.m_skillGainRate;
			value = ((num9 > 0f) ? (num8 / num9) : 0f);
			float num10 = Mathf.Max(0f, debt - num6);
			SetDebt(val, skillType, num10);
			LogDebug($"Debt paydown: {skillType} paid {num6:F4} (intended {num5:F4}), debt {debt:F4}->{num10:F4}, " + $"rerouted {num7:F4} units to leveling, forwarded factor {value:F4}");
			if (debt > 0f && num10 <= 0f)
			{
				ClearFocusPaydownForSkill(val, skillType);
				DebtClearedFeedback.TryPlay(val);
				MessageHud instance = MessageHud.instance;
				if (instance != null)
				{
					instance.ShowMessage((MessageType)2, $"{skillType} debt repaid", 0, (Sprite)null, false, true);
				}
			}
			return true;
		}

		internal static float GetDeathFactor(Skills skills)
		{
			return ((DeathLowerFactorField != null) ? ((float)DeathLowerFactorField.GetValue(skills)) : 0f) * Game.m_skillReductionRate;
		}

		private static float GetSkillLevel(object skillObj)
		{
			if (!(SkillLevelField != null))
			{
				return 0f;
			}
			return (float)SkillLevelField.GetValue(skillObj);
		}

		private static float GetIncreaseStep(Skill skill)
		{
			object obj = SkillInfoField?.GetValue(skill);
			if (obj == null || !(SkillIncreaseStepField != null))
			{
				return 0f;
			}
			return (float)SkillIncreaseStepField.GetValue(obj);
		}

		private static Skill GetSkillFromMap(Skills skills, SkillType skillType)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if (!(SkillsDataField?.GetValue(skills) is IDictionary dictionary) || !dictionary.Contains(skillType))
			{
				return null;
			}
			object? obj = dictionary[skillType];
			return (Skill)((obj is Skill) ? obj : null);
		}

		private unsafe static string GetDebtKey(SkillType skillType)
		{
			return "Skuld_Debt_" + ((object)(*(SkillType*)(&skillType))/*cast due to .constrained prefix*/).ToString();
		}

		private unsafe static string GetBaselineKey(SkillType skillType)
		{
			return "Skuld_DebtBaseline_" + ((object)(*(SkillType*)(&skillType))/*cast due to .constrained prefix*/).ToString();
		}

		private unsafe static string GetIncurredKey(SkillType skillType)
		{
			return "Skuld_DebtIncurred_" + ((object)(*(SkillType*)(&skillType))/*cast due to .constrained prefix*/).ToString();
		}

		internal static float GetDebt(Player player, SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return GetCustomFloat(player, GetDebtKey(skillType));
		}

		internal static float GetBaseline(Player player, SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return GetCustomFloat(player, GetBaselineKey(skillType));
		}

		internal static float GetIncurred(Player player, SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return GetCustomFloat(player, GetIncurredKey(skillType));
		}

		internal static float GetPaidOff(Player player, SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			EnsureProgressConsistency(player, skillType);
			return Mathf.Max(0f, GetBaseline(player, skillType) - GetDebt(player, skillType));
		}

		internal static string FormatProgressDetail(Player player, SkillType skillType)
		{
			//IL_0001: 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_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			EnsureProgressConsistency(player, skillType);
			float debt = GetDebt(player, skillType);
			float paidOff = GetPaidOff(player, skillType);
			string arg = (IsFocusPaydownActive(player, skillType) ? "  [focused 100%]" : string.Empty);
			return $"-{debt:0.0000}  ({paidOff:0.0000} paid off){arg}";
		}

		internal static float GetPaydownShare(Player player, SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			if (IsFocusPaydownActive(player, skillType))
			{
				return 1f;
			}
			return ModConfig.GetPaydownShare();
		}

		internal static bool IsFocusAll(Player player)
		{
			return GetCustomFlag(player, "Skuld_FocusPaydown_All");
		}

		internal static bool IsFocusPaydownActive(Player player, SkillType skillType)
		{
			//IL_0009: 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_0017: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || (int)skillType == 0)
			{
				return false;
			}
			if (IsFocusAll(player))
			{
				return GetDebt(player, skillType) > 0f;
			}
			return GetCustomFlag(player, GetFocusPaydownKey(skillType));
		}

		internal static bool TryEnableFocusAll(Player player, out string message)
		{
			message = string.Empty;
			if (player?.m_customData == null)
			{
				return false;
			}
			ClearPerSkillFocusFlags(player);
			SetCustomFlag(player, "Skuld_FocusPaydown_All", value: true);
			message = "Focus all enabled — 100% paydown on skills with debt until cleared.";
			return true;
		}

		internal static bool TryEnableFocusSkill(Player player, SkillType skillType, out string message)
		{
			//IL_0015: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			message = string.Empty;
			if (player?.m_customData == null || (int)skillType == 0)
			{
				return false;
			}
			SetCustomFlag(player, "Skuld_FocusPaydown_All", value: false);
			SetCustomFlag(player, GetFocusPaydownKey(skillType), value: true);
			message = FormatSkillName(skillType) + " focus enabled — 100% paydown until that debt is cleared.";
			return true;
		}

		internal static void ClearAllFocusPaydown(Player player)
		{
			if (player?.m_customData != null)
			{
				SetCustomFlag(player, "Skuld_FocusPaydown_All", value: false);
				ClearPerSkillFocusFlags(player);
			}
		}

		internal static void ShowFocusOverview(Player player, Terminal context)
		{
			if ((Object)(object)player == (Object)null)
			{
				if (context != null)
				{
					context.AddString("No local player.");
				}
				return;
			}
			PruneInactiveFocusFlags(player);
			if (IsFocusAll(player))
			{
				List<string> indebtedSkillNames = GetIndebtedSkillNames(player);
				context.AddString("Focus all: active — 100% paydown on skills with debt.");
				if (indebtedSkillNames.Count > 0)
				{
					context.AddString("  Indebted: " + string.Join(", ", indebtedSkillNames));
				}
				else
				{
					context.AddString("  No outstanding debt right now.");
				}
				return;
			}
			List<string> focusedSkillNames = GetFocusedSkillNames(player);
			if (focusedSkillNames.Count == 0)
			{
				context.AddString("No focused paydown skills. Use /focus <skill|all> or /focus off.");
				return;
			}
			context.AddString($"Focused paydown ({focusedSkillNames.Count}) — 100% until cleared:");
			foreach (string item in focusedSkillNames)
			{
				context.AddString("  " + item);
			}
		}

		private static void ClearFocusPaydownForSkill(Player player, SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			SetCustomFlag(player, GetFocusPaydownKey(skillType), value: false);
			if (IsFocusAll(player) && !HasAnyDebt(player))
			{
				SetCustomFlag(player, "Skuld_FocusPaydown_All", value: false);
			}
		}

		private unsafe static string GetFocusPaydownKey(SkillType skillType)
		{
			return "Skuld_FocusPaydown_" + ((object)(*(SkillType*)(&skillType))/*cast due to .constrained prefix*/).ToString();
		}

		private static void ClearPerSkillFocusFlags(Player player)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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)
			foreach (SkillType value in Enum.GetValues(typeof(SkillType)))
			{
				if ((int)value != 0)
				{
					SetCustomFlag(player, GetFocusPaydownKey(value), value: false);
				}
			}
		}

		private static void PruneInactiveFocusFlags(Player player)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			foreach (SkillType value in Enum.GetValues(typeof(SkillType)))
			{
				if ((int)value != 0 && GetCustomFlag(player, GetFocusPaydownKey(value)) && GetDebt(player, value) <= 0f)
				{
					SetCustomFlag(player, GetFocusPaydownKey(value), value: false);
				}
			}
			if (IsFocusAll(player) && !HasAnyDebt(player))
			{
				SetCustomFlag(player, "Skuld_FocusPaydown_All", value: false);
			}
		}

		private static bool HasAnyDebt(Player player)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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)
			foreach (SkillType value in Enum.GetValues(typeof(SkillType)))
			{
				if ((int)value != 0 && GetDebt(player, value) > 0f)
				{
					return true;
				}
			}
			return false;
		}

		private static List<string> GetIndebtedSkillNames(Player player)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string>();
			foreach (SkillType value in Enum.GetValues(typeof(SkillType)))
			{
				if ((int)value != 0 && GetDebt(player, value) > 0f)
				{
					list.Add(FormatSkillName(value));
				}
			}
			return list;
		}

		private static List<string> GetFocusedSkillNames(Player player)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string>();
			foreach (SkillType value in Enum.GetValues(typeof(SkillType)))
			{
				if ((int)value != 0 && GetCustomFlag(player, GetFocusPaydownKey(value)))
				{
					string text = ((GetDebt(player, value) > 0f) ? string.Empty : " (no debt)");
					list.Add(FormatSkillName(value) + text);
				}
			}
			return list;
		}

		private static bool GetCustomFlag(Player player, string key)
		{
			if (player?.m_customData == null)
			{
				return false;
			}
			if (player.m_customData.TryGetValue(key, out var value))
			{
				return string.Equals(value, "1", StringComparison.Ordinal);
			}
			return false;
		}

		private static void SetCustomFlag(Player player, string key, bool value)
		{
			if (player?.m_customData != null)
			{
				if (value)
				{
					player.m_customData[key] = "1";
				}
				else
				{
					player.m_customData.Remove(key);
				}
			}
		}

		internal static float GetLifetimeRepaid(Player player, SkillType skillType)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			return Mathf.Max(0f, GetIncurred(player, skillType) - GetDebt(player, skillType));
		}

		internal static string FormatLifetimeDetail(Player player, SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			float incurred = GetIncurred(player, skillType);
			float lifetimeRepaid = GetLifetimeRepaid(player, skillType);
			return $"lifetime debt {incurred:0.0000}, lifetime repaid {lifetimeRepaid:0.0000}";
		}

		private static void EnsureProgressConsistency(Player player, SkillType skillType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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_003f: Unknown result type (might be due to invalid IL or missing references)
			float debt = GetDebt(player, skillType);
			float num = GetIncurred(player, skillType);
			float baseline = GetBaseline(player, skillType);
			if (debt > 0f && num < debt)
			{
				SetIncurred(player, skillType, debt);
				num = debt;
			}
			if (debt > 0f && baseline <= 0f)
			{
				SetBaseline(player, skillType, Mathf.Max(debt, num));
			}
		}

		private static float GetCustomFloat(Player player, string key)
		{
			if (player?.m_customData == null)
			{
				return 0f;
			}
			if (!player.m_customData.TryGetValue(key, out var value) || string.IsNullOrEmpty(value))
			{
				return 0f;
			}
			if (!float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return 0f;
			}
			return Mathf.Max(0f, result);
		}

		private static void SetDebt(Player player, SkillType skillType, float value)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			SetCustomFloat(player, GetDebtKey(skillType), value);
		}

		private static void SetBaseline(Player player, SkillType skillType, float value)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			SetCustomFloat(player, GetBaselineKey(skillType), value);
		}

		private static void SetIncurred(Player player, SkillType skillType, float value)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			SetCustomFloat(player, GetIncurredKey(skillType), value);
		}

		private static void SetCustomFloat(Player player, string key, float value)
		{
			if (player?.m_customData != null)
			{
				float num = Mathf.Max(0f, value);
				if (num <= 0f)
				{
					player.m_customData.Remove(key);
				}
				else
				{
					player.m_customData[key] = num.ToString("R", CultureInfo.InvariantCulture);
				}
			}
		}

		internal static List<string> ClearAllDebt(Player player)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string>();
			if (player?.m_customData == null)
			{
				return list;
			}
			foreach (SkillType value in Enum.GetValues(typeof(SkillType)))
			{
				if ((int)value == 0)
				{
					continue;
				}
				float debt = GetDebt(player, value);
				float incurred = GetIncurred(player, value);
				float baseline = GetBaseline(player, value);
				if (!(debt <= 0f) || !(incurred <= 0f) || !(baseline <= 0f))
				{
					SetDebt(player, value, 0f);
					SetBaseline(player, value, 0f);
					SetIncurred(player, value, 0f);
					SetCustomFlag(player, GetFocusPaydownKey(value), value: false);
					if (debt > 0f)
					{
						list.Add($"{FormatSkillName(value)} {debt:0.0}");
					}
				}
			}
			SetCustomFlag(player, "Skuld_FocusPaydown_All", value: false);
			return list;
		}

		internal static void ShowAllDebtOverview(Player player, Terminal context)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				if (context != null)
				{
					context.AddString("No local player.");
				}
				return;
			}
			List<string> list = new List<string>();
			foreach (SkillType value in Enum.GetValues(typeof(SkillType)))
			{
				if ((int)value != 0)
				{
					EnsureProgressConsistency(player, value);
					float debt = GetDebt(player, value);
					if (!(debt <= 0f))
					{
						list.Add($"{FormatSkillName(value)} -{Mathf.RoundToInt(debt)}");
					}
				}
			}
			if (list.Count == 0)
			{
				if (context != null)
				{
					context.AddString("Skuld: no outstanding skill debt.");
				}
				MessageHud instance = MessageHud.instance;
				if (instance != null)
				{
					instance.ShowMessage((MessageType)1, "No outstanding skill debt.", 0, (Sprite)null, false, true);
				}
				return;
			}
			if (context != null)
			{
				context.AddString($"Skuld debt ({list.Count}):");
			}
			foreach (string item in list)
			{
				if (context != null)
				{
					context.AddString("  " + item);
				}
			}
			if (context != null)
			{
				context.AddString("Exact progress: /show debt <skill>");
			}
			MessageHud instance2 = MessageHud.instance;
			if (instance2 != null)
			{
				instance2.ShowMessage((MessageType)1, $"Debt on {list.Count} skills — list printed to chat.", 0, (Sprite)null, false, true);
			}
		}

		internal static void ShowAllLifetimeOverview(Player player, Terminal context)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				if (context != null)
				{
					context.AddString("No local player.");
				}
				return;
			}
			List<string> list = new List<string>();
			foreach (SkillType value in Enum.GetValues(typeof(SkillType)))
			{
				if ((int)value != 0)
				{
					float incurred = GetIncurred(player, value);
					if (!(incurred <= 0f))
					{
						list.Add($"{FormatSkillName(value)} {incurred:0.0000}");
					}
				}
			}
			if (list.Count == 0)
			{
				if (context != null)
				{
					context.AddString("Skuld: no lifetime debt recorded.");
				}
				MessageHud instance = MessageHud.instance;
				if (instance != null)
				{
					instance.ShowMessage((MessageType)1, "No lifetime debt recorded.", 0, (Sprite)null, false, true);
				}
				return;
			}
			if (context != null)
			{
				context.AddString($"Skuld lifetime debt ({list.Count}):");
			}
			foreach (string item in list)
			{
				if (context != null)
				{
					context.AddString("  " + item);
				}
			}
			MessageHud instance2 = MessageHud.instance;
			if (instance2 != null)
			{
				instance2.ShowMessage((MessageType)1, $"Lifetime debt on {list.Count} skills — list printed to chat.", 0, (Sprite)null, false, true);
			}
		}

		internal static bool TryResolveSkillType(string rawName, out SkillType skillType)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected I4, but got Unknown
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			skillType = (SkillType)0;
			if (string.IsNullOrWhiteSpace(rawName))
			{
				return false;
			}
			string b = rawName.Replace(" ", string.Empty).Replace("_", string.Empty);
			foreach (SkillType value in Enum.GetValues(typeof(SkillType)))
			{
				if ((int)value != 0)
				{
					string a = ((object)value/*cast due to .constrained prefix*/).ToString();
					if (string.Equals(a, rawName, StringComparison.OrdinalIgnoreCase) || string.Equals(a, b, StringComparison.OrdinalIgnoreCase) || string.Equals(FormatSkillName(value).Replace(" ", string.Empty), b, StringComparison.OrdinalIgnoreCase))
					{
						skillType = (SkillType)(int)value;
						return true;
					}
				}
			}
			return false;
		}

		internal unsafe static string FormatSkillName(SkillType skillType)
		{
			string text = ((object)(*(SkillType*)(&skillType))/*cast due to .constrained prefix*/).ToString();
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(text[0]);
			for (int i = 1; i < text.Length; i++)
			{
				char c = text[i];
				if (char.IsUpper(c) && !char.IsUpper(text[i - 1]))
				{
					stringBuilder.Append(' ');
				}
				stringBuilder.Append(c);
			}
			return stringBuilder.ToString();
		}

		private static string DescribePlayer(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return "null";
			}
			return $"name={player.GetPlayerName()}, id={player.GetPlayerID()}, isLocal={(Object)(object)player == (Object)(object)Player.m_localPlayer}";
		}

		private static float GetNextLevelRequirement(float currentLevel)
		{
			return Mathf.Pow(Mathf.Floor(currentLevel + 1f), 1.5f) * 0.5f + 0.5f;
		}

		private static void LogDebug(string message)
		{
			if (ModConfig.DebugLogging.Value)
			{
				Plugin.Log.LogInfo((object)message);
			}
		}

		private static string BuildDeathSummary(List<string> entries)
		{
			if (entries.Count <= 6)
			{
				return "Debt: " + string.Join(", ", entries);
			}
			return $"Debt ({entries.Count} skills): " + string.Join(", ", entries.GetRange(0, 6)) + $" (+{entries.Count - 6} more; /show debt all)";
		}
	}
	internal static class SkillLimitExtenderCompat
	{
		private static bool lookupAttempted;

		private static MethodInfo getUiDenominatorMethod;

		internal static bool TryGetUiDenominator(SkillType skillType, out float denominator)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			denominator = 0f;
			if (!TryResolveMethod())
			{
				return false;
			}
			try
			{
				object obj = getUiDenominatorMethod.Invoke(null, new object[1] { skillType });
				if (obj is float num && num > 0f)
				{
					denominator = num;
					return true;
				}
				if (obj is double num2 && num2 > 0.0)
				{
					denominator = (float)num2;
					return true;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogDebug((object)$"SkillLimitExtender UI denominator lookup failed for {skillType}: {ex.Message}");
			}
			return false;
		}

		private static bool TryResolveMethod()
		{
			if (lookupAttempted)
			{
				return getUiDenominatorMethod != null;
			}
			lookupAttempted = true;
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if ((assembly.GetName().Name ?? string.Empty).IndexOf("SkillLimitExtender", StringComparison.OrdinalIgnoreCase) < 0)
				{
					continue;
				}
				Type type = assembly.GetType("SkillLimitExtender.SkillConfigManager") ?? assembly.GetType("SkillConfigManager");
				if (!(type == null))
				{
					getUiDenominatorMethod = type.GetMethod("GetUiDenominatorForSkillSafe", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(object) }, null);
					if (getUiDenominatorMethod != null)
					{
						return true;
					}
				}
			}
			return false;
		}
	}
}
namespace Skuld.Patches
{
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class PlayerOnDeathDiagnosticsPatch
	{
		private static readonly FieldInfo TimeSinceDeathField = AccessTools.Field(typeof(Player), "m_timeSinceDeath");

		private static readonly MethodInfo HardDeathMethod = AccessTools.Method(typeof(Player), "HardDeath", (Type[])null, (Type[])null);

		private static void Prefix(Player __instance)
		{
			float num = ((TimeSinceDeathField != null) ? ((float)TimeSinceDeathField.GetValue(__instance)) : (-1f));
			float num2 = (((Object)(object)__instance != (Object)null) ? __instance.m_hardDeathCooldown : (-1f));
			bool flag = HardDeathMethod != null && (Object)(object)__instance != (Object)null && (bool)HardDeathMethod.Invoke(__instance, null);
			Plugin.Log.LogInfo((object)$"Skuld diagnostic: Player.OnDeath entered. hardDeath={flag}, m_timeSinceDeath={num:F3}, m_hardDeathCooldown={num2:F3}");
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class PlayerSoftDeathClarityPatch
	{
		private const string SoftDeathDebtMessage = "Soft death — no skill debt";

		private static readonly MethodInfo HardDeathMethod = AccessTools.Method(typeof(Player), "HardDeath", (Type[])null, (Type[])null);

		private static bool isHardDeath;

		private static void Prefix(Player __instance)
		{
			isHardDeath = HardDeathMethod != null && (Object)(object)__instance != (Object)null && (bool)HardDeathMethod.Invoke(__instance, null);
		}

		private static void Postfix(Player __instance)
		{
			if (ModConfig.IsModEnabled && !isHardDeath && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				((Character)__instance).Message((MessageType)1, "Soft death — no skill debt", 0, (Sprite)null, false);
			}
		}
	}
	[HarmonyPatch(typeof(SkillsDialog), "Setup")]
	internal static class SkillsDialogSetupPatch
	{
		private static readonly FieldInfo ElementsField = AccessTools.Field(typeof(SkillsDialog), "m_elements");

		private static readonly FieldInfo TooltipAnchorField = AccessTools.Field(typeof(SkillsDialog), "m_tooltipAnchor");

		private static void Postfix(SkillsDialog __instance, Player player)
		{
			if (!ModConfig.IsModEnabled || (Object)(object)player == (Object)null || ElementsField == null)
			{
				return;
			}
			if (!(ElementsField.GetValue(__instance) is List<GameObject> list))
			{
				Plugin.Log.LogWarning((object)"Skuld skill UI: could not read SkillsDialog.m_elements.");
				return;
			}
			object? obj = TooltipAnchorField?.GetValue(__instance);
			RectTransform tooltipAnchor = (RectTransform)((obj is RectTransform) ? obj : null);
			List<Skill> skillList = ((Character)player).GetSkills().GetSkillList();
			int num = Mathf.Min(skillList.Count, list.Count);
			for (int i = 0; i < num; i++)
			{
				Skill val = skillList[i];
				GameObject val2 = list[i];
				if (val?.m_info != null && !((Object)(object)val2 == (Object)null) && val2.activeSelf)
				{
					ApplyDebtDisplay(val2, player, val, tooltipAnchor);
				}
			}
		}

		private static void ApplyDebtDisplay(GameObject row, Player player, Skill skill, RectTransform tooltipAnchor)
		{
			SkillDebtBarOverlay.Apply(row.transform, player, skill);
			BindRowDebtTooltip(row, skill, tooltipAnchor);
		}

		private static void BindRowDebtTooltip(GameObject row, Skill skill, RectTransform tooltipAnchor)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			UITooltip componentInChildren = row.GetComponentInChildren<UITooltip>(true);
			if (!((Object)(object)componentInChildren == (Object)null))
			{
				Transform transform = row.transform;
				RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
				Vector2 position = (Vector2)(((Object)(object)val != (Object)null) ? new Vector2(0f, Mathf.Min(255f, ((Transform)val).localPosition.y + 10f)) : Vector2.zero);
				DebtTooltipBinder debtTooltipBinder = row.GetComponent<DebtTooltipBinder>();
				if ((Object)(object)debtTooltipBinder == (Object)null)
				{
					debtTooltipBinder = row.AddComponent<DebtTooltipBinder>();
				}
				debtTooltipBinder.Initialize(skill.m_info.m_skill, skill, componentInChildren, skill.m_info.m_description, tooltipAnchor, position);
			}
		}
	}
	[HarmonyPatch(typeof(Skills), "OnDeath")]
	internal static class SkillsOnDeathPatch
	{
		private static bool Prefix(Skills __instance)
		{
			Plugin.Log.LogInfo((object)"Skuld patch entry: Skills.OnDeath prefix fired.");
			SkillDebtService.LogHardDeathDiagnostics(__instance);
			if (!ModConfig.IsModEnabled || !SkillDebtService.IsReady)
			{
				return true;
			}
			if (!SkillDebtService.TryConvertDeathToDebt(__instance, out var deathSummary))
			{
				Plugin.Log.LogWarning((object)"Skuld could not convert death penalty to debt. Falling back to vanilla skill loss.");
				return true;
			}
			if (!string.IsNullOrEmpty(deathSummary))
			{
				object? obj = AccessTools.Field(typeof(Skills), "m_player")?.GetValue(__instance);
				object? obj2 = ((obj is Player) ? obj : null);
				if (obj2 != null)
				{
					((Character)obj2).Message((MessageType)1, deathSummary, 0, (Sprite)null, false);
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Skills), "RaiseSkill")]
	internal static class SkillsRaiseSkillPatch
	{
		private static bool hasLoggedEntry;

		private static v