Decompiled source of Skuld v0.2.0

Skuld.dll

Decompiled 18 hours 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("0.2.0.0")]
[assembly: AssemblyInformationalVersion("0.2.0+ca5a53799910255b1d65a6275d02de7881b295e8")]
[assembly: AssemblyProduct("Skuld")]
[assembly: AssemblyTitle("Skuld")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.2.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 ModConfig
	{
		internal static ConfigEntry<bool> EnableMod { get; private set; }

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

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

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

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

		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
			EnableMod = config.Bind<bool>("General", "EnableMod", true, "Enable Skuld debt mechanics.");
			DebtPaydownShare = config.Bind<float>("General", "DebtPaydownShare", 0.5f, new ConfigDescription("Fraction of earned XP redirected to debt paydown while debt remains.", (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.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 5), Array.Empty<object>()));
			DebugLogging = config.Bind<bool>("General", "DebugLogging", false, "Enable verbose debt conversion/paydown logs.");
			EnableDevCommands = config.Bind<bool>("Dev", "EnableDevCommands", false, "Enable Skuld test console commands. Must remain false for Thunderstore releases.");
		}

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

		internal static int GetMaxDebtPerSkill()
		{
			return Mathf.Clamp(MaxDebtPerSkill.Value, 0, 5);
		}
	}
	internal static class ModConstants
	{
		internal const string ModGuid = "com.cdjensen99.skuld";

		internal const string ModName = "Skuld";

		internal const string ModVersion = "0.2.0";

		internal const string DebtKeyPrefix = "Skuld_Debt_";

		internal const string DebtBaselineKeyPrefix = "Skuld_DebtBaseline_";

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

		private Harmony harmony;

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

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

		private static void LogPatchStatus()
		{
			LogOriginalPatchStatus(typeof(Skills), "OnDeath");
			LogOriginalPatchStatus(typeof(Player), "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 sealed class DebtTooltipBinder : MonoBehaviour
	{
		private SkillType skillType;

		private UITooltip tooltip;

		private RectTransform tooltipAnchor;

		private Vector2 fixedPosition;

		private string baseDescription = string.Empty;

		internal void Initialize(SkillType type, 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_0027: 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)
			skillType = type;
			tooltip = tip;
			baseDescription = description ?? string.Empty;
			tooltipAnchor = anchor;
			fixedPosition = position;
			Refresh();
		}

		private void LateUpdate()
		{
			Refresh();
		}

		private void Refresh()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: 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)
			if (!((Object)(object)tooltip == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				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_0045: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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, false, false, false, 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, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			registered = true;
			string text = (ModConfig.EnableDevCommands.Value ? "enabled" : "disabled (set EnableDevCommands=true in com.cdjensen99.skuld.cfg)");
			Plugin.Log.LogInfo((object)("Skuld dev console commands registered (" + text + "): skuld_clearcooldown, skuld_cleardebt."));
		}

		private static void ClearCooldown(ConsoleEventArgs args)
		{
			if (!ModConfig.EnableDevCommands.Value)
			{
				args.Context.AddString("Skuld dev commands are disabled. Set EnableDevCommands=true in BepInEx/config/com.cdjensen99.skuld.cfg.");
				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.EnableDevCommands.Value)
			{
				args.Context.AddString("Skuld dev commands are disabled. Set EnableDevCommands=true in BepInEx/config/com.cdjensen99.skuld.cfg.");
				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>__GetSkillTabOptions;
		}

		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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: 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>__GetSkillTabOptions;
				if (obj2 == null)
				{
					ConsoleOptionsFetcher val2 = GetSkillTabOptions;
					<>O.<1>__GetSkillTabOptions = val2;
					obj2 = (object)val2;
				}
				new ConsoleCommand("show", "Skuld: /show debt [skill|all|lifetime ...] — show debt progress.", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)obj2, false, false, false);
				registered = true;
				Plugin.Log.LogInfo((object)"Skuld player chat command registered: /show debt [skill|all|lifetime ...]");
			}
		}

		private static void ShowCommand(ConsoleEventArgs args)
		{
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: 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_014e: Unknown result type (might be due to invalid IL or missing references)
			if (args.Length < 2 || !string.Equals(args[1], "debt", StringComparison.OrdinalIgnoreCase))
			{
				args.Context.AddString("Usage: /show debt [skill|all|lifetime <skill|all>]");
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				args.Context.AddString("No local player.");
				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);
			}
		}

		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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			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;
			}
			if (SkillDebtService.GetIncurred(player, skillType) <= 0f)
			{
				context.AddString(SkillDebtService.FormatSkillName(skillType) + ": no lifetime debt recorded.");
				return;
			}
			string text2 = SkillDebtService.FormatLifetimeDetail(player, skillType);
			context.AddString(SkillDebtService.FormatSkillName(skillType) + " lifetime: " + text2);
			MessageHud instance = MessageHud.instance;
			if (instance != null)
			{
				instance.ShowMessage((MessageType)1, SkillDebtService.FormatSkillName(skillType) + " lifetime " + text2, 0, (Sprite)null, false);
			}
		}

		private static List<string> GetSkillTabOptions()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string> { "debt", "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;
		}
	}
	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(Player player, SkillType skillType, ref float value)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: 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_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			if (value <= 0f || (int)skillType == 0 || (Object)(object)player == (Object)null)
			{
				return true;
			}
			EnsureProgressConsistency(player, skillType);
			float debt = GetDebt(player, skillType);
			if (debt <= 0f)
			{
				return true;
			}
			Skills skills = ((Character)player).GetSkills();
			Skill val = (((Object)(object)skills != (Object)null) ? GetSkillFromMap(skills, skillType) : null);
			if (val == null)
			{
				return true;
			}
			float increaseStep = GetIncreaseStep(val);
			if (increaseStep <= 0f)
			{
				return true;
			}
			float num = value;
			float num2 = 1f;
			((Character)player).GetSEMan().ModifyRaiseSkill(skillType, ref num2);
			float num3 = num * num2;
			float num4 = increaseStep * num3 * Game.m_skillGainRate;
			float paydownShare = ModConfig.GetPaydownShare();
			float num5 = 1f - paydownShare;
			float nextLevelRequirement = GetNextLevelRequirement(val.m_level);
			float num6 = num4 * paydownShare;
			float num7 = num4 * num5;
			float num8 = ((nextLevelRequirement > 0f) ? (num6 / nextLevelRequirement) : 0f);
			float num9 = Mathf.Min(debt, num8);
			float num10 = num6 - num9 * nextLevelRequirement;
			float num11 = num7 + num10;
			float num12 = increaseStep * num2 * Game.m_skillGainRate;
			value = ((num12 > 0f) ? (num11 / num12) : 0f);
			float num13 = Mathf.Max(0f, debt - num9);
			SetDebt(player, skillType, num13);
			LogDebug($"Debt paydown: {skillType} paid {num9:F4} (intended {num8:F4}), debt {debt:F4}->{num13:F4}, " + $"rerouted {num10:F4} units to leveling, forwarded pre-multiplier value {value:F4}");
			if (debt > 0f && num13 <= 0f && (Object)(object)MessageHud.instance != (Object)null)
			{
				MessageHud.instance.ShowMessage((MessageType)2, $"{skillType} debt repaid", 0, (Sprite)null, false);
			}
			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)
			EnsureProgressConsistency(player, skillType);
			float debt = GetDebt(player, skillType);
			float paidOff = GetPaidOff(player, skillType);
			return $"-{debt:0.0000}  ({paidOff:0.0000} paid off)";
		}

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

		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_0040: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: 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);
					if (debt > 0f)
					{
						list.Add($"{FormatSkillName(value)} {debt:0.0}");
					}
				}
			}
			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);
				}
				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);
			}
		}

		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);
				}
				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);
			}
		}

		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)";
		}
	}
}
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), "RaiseSkill")]
	internal static class PlayerRaiseSkillPatch
	{
		private static bool hasLoggedEntry;

		private static void Prefix(Player __instance, SkillType skill, ref float value)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			if (!hasLoggedEntry)
			{
				Plugin.Log.LogInfo((object)"Skuld patch entry: Player.RaiseSkill prefix fired.");
				hasLoggedEntry = true;
			}
			if (ModConfig.EnableMod.Value && SkillDebtService.IsReady)
			{
				SkillDebtService.TryApplyRaiseSkillDebt(__instance, skill, ref value);
			}
		}
	}
	[HarmonyPatch(typeof(SkillsDialog), "Setup")]
	internal static class SkillsDialogSetupPatch
	{
		private const string DebtTextName = "skuld_debttext";

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

		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.EnableMod.Value || (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)
				{
					ApplyDebtLabel(val2, player, val, tooltipAnchor);
				}
			}
		}

		private static void ApplyDebtLabel(GameObject row, Player player, Skill skill, RectTransform tooltipAnchor)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: 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_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			SkillType skill2 = skill.m_info.m_skill;
			Transform val = Utils.FindChild(row.transform, "bonustext", (IterativeSearchType)0);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			TMP_Text component = ((Component)val).GetComponent<TMP_Text>();
			TMP_Text orCreateDebtText = GetOrCreateDebtText(val, component);
			if ((Object)(object)orCreateDebtText == (Object)null)
			{
				return;
			}
			float debt = SkillDebtService.GetDebt(player, skill2);
			bool flag = debt > 0f;
			((Component)orCreateDebtText).gameObject.SetActive(flag);
			if (flag)
			{
				orCreateDebtText.text = (-Mathf.RoundToInt(debt)).ToString("0");
				((Graphic)orCreateDebtText).color = DebtColor;
				RectTransform rectTransform = orCreateDebtText.rectTransform;
				RectTransform val2 = (RectTransform)(object)((val is RectTransform) ? val : null);
				if ((Object)(object)rectTransform != (Object)null && (Object)(object)val2 != (Object)null)
				{
					float num;
					if (!((Component)val).gameObject.activeSelf)
					{
						num = 0f;
					}
					else
					{
						Rect rect = val2.rect;
						num = ((Rect)(ref rect)).width + 8f;
					}
					float num2 = num;
					rectTransform.anchoredPosition = val2.anchoredPosition + new Vector2(num2, 0f);
				}
			}
			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_0084: 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, componentInChildren, skill.m_info.m_description, tooltipAnchor, position);
			}
		}

		private static TMP_Text GetOrCreateDebtText(Transform bonusTransform, TMP_Text bonusText)
		{
			//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_00dc: Unknown result type (might be due to invalid IL or missing references)
			Transform val = bonusTransform.parent.Find("skuld_debttext");
			if ((Object)(object)val != (Object)null)
			{
				return ((Component)val).GetComponent<TMP_Text>();
			}
			GameObject val2 = Object.Instantiate<GameObject>(((Component)bonusTransform).gameObject, bonusTransform.parent);
			((Object)val2).name = "skuld_debttext";
			UITooltip component = val2.GetComponent<UITooltip>();
			if ((Object)(object)component != (Object)null)
			{
				Object.Destroy((Object)(object)component);
			}
			TMP_Text component2 = val2.GetComponent<TMP_Text>();
			if ((Object)(object)component2 == (Object)null)
			{
				Object.Destroy((Object)(object)val2);
				return null;
			}
			if ((Object)(object)bonusText != (Object)null)
			{
				component2.font = bonusText.font;
				component2.fontSize = bonusText.fontSize;
				component2.fontStyle = bonusText.fontStyle;
				component2.alignment = bonusText.alignment;
				component2.enableAutoSizing = bonusText.enableAutoSizing;
			}
			CanvasGroup val3 = val2.GetComponent<CanvasGroup>();
			if ((Object)(object)val3 == (Object)null)
			{
				val3 = val2.AddComponent<CanvasGroup>();
			}
			val3.blocksRaycasts = false;
			val3.interactable = false;
			((Graphic)component2).color = DebtColor;
			val2.SetActive(false);
			return component2;
		}
	}
	[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.EnableMod.Value || !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);
				}
			}
			return false;
		}
	}
}