Decompiled source of SkillKit v0.1.0

SkillKit/SkillKit.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Logging;
using ForgeKit;
using SideLoader;
using SideLoader.Model;
using UnityEngine;
using UnityEngine.SceneManagement;

[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("SkillKit")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+c48b2eb6460f1b993d9ddb914cbdc5ff6f1d22c6")]
[assembly: AssemblyProduct("SkillKit")]
[assembly: AssemblyTitle("SkillKit")]
[assembly: AssemblyMetadata("BuildStamp", "c48b2eb 2026-07-30")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
namespace SkillKit;

internal static class CastAnimEngine
{
	private struct ParsedAnim
	{
		public string AnimName;

		public bool Valid;

		public SpellCastType Type;
	}

	private struct LastSync
	{
		public Character Player;

		public SpellCastType Type;

		public SpellCastModifier Modifier;

		public bool Primed;
	}

	private static readonly HashSet<SkillSpec> _warnedBadName = new HashSet<SkillSpec>();

	private static readonly HashSet<SkillSpec> _warnedBowAttack = new HashSet<SkillSpec>();

	private static readonly Dictionary<SkillSpec, ParsedAnim> _parsed = new Dictionary<SkillSpec, ParsedAnim>();

	private static readonly Dictionary<SkillSpec, LastSync> _lastSync = new Dictionary<SkillSpec, LastSync>();

	internal static void InvalidateConverged()
	{
		_lastSync.Clear();
	}

	internal static void Sync(SkillSpec spec, Character player)
	{
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Invalid comparison between Unknown and I4
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_011e: Invalid comparison between Unknown and I4
		//IL_01df: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0154: Unknown result type (might be due to invalid IL or missing references)
		//IL_0220: Unknown result type (might be due to invalid IL or missing references)
		//IL_022b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0182: Unknown result type (might be due to invalid IL or missing references)
		//IL_0188: Unknown result type (might be due to invalid IL or missing references)
		//IL_0191: 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)
		if (spec.CastAnim == null)
		{
			return;
		}
		CastPick castPick = spec.CastAnim(player);
		if (!_parsed.TryGetValue(spec, out var value) || (object)value.AnimName != castPick.AnimName)
		{
			value.AnimName = castPick.AnimName;
			value.Valid = Enum.TryParse<SpellCastType>(castPick.AnimName, ignoreCase: true, out value.Type);
			_parsed[spec] = value;
		}
		if (!value.Valid)
		{
			if (_warnedBadName.Add(spec))
			{
				ManualLogSource log = SkillRegistry.Log;
				if (log != null)
				{
					log.LogWarning((object)("[SKILLKIT] '" + spec.Label + "': '" + castPick.AnimName + "' isn't a real Character.SpellCastType — cast-sync skipped, the donor's baked cast will show instead."));
				}
			}
			return;
		}
		_warnedBadName.Remove(spec);
		SpellCastType type = value.Type;
		SpellCastModifier modifier = castPick.Modifier;
		if ((Object)(object)((player != null) ? player.CurrentWeapon : null) != (Object)null && (int)player.CurrentWeapon.Type == 200 && (int)modifier == 2)
		{
			if (_warnedBowAttack.Add(spec))
			{
				ManualLogSource log2 = SkillRegistry.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("[SKILLKIT] '" + spec.Label + "': CastModifier.Attack with a bow equipped crashes AttackInput's charge path (bug-13) — forcing Immobilized instead."));
				}
			}
			modifier = (SpellCastModifier)0;
		}
		if (_lastSync.TryGetValue(spec, out var value2) && value2.Primed && (Object)(object)value2.Player == (Object)(object)player && value2.Type == type && value2.Modifier == modifier)
		{
			return;
		}
		var (num, num2) = SkillInstanceSync.Sync(spec.ItemId, player, (Item item) => Apply(item, type, modifier));
		_lastSync[spec] = new LastSync
		{
			Player = player,
			Type = type,
			Modifier = modifier,
			Primed = true
		};
		if (num > 0)
		{
			ManualLogSource log3 = SkillRegistry.Log;
			if (log3 != null)
			{
				log3.LogMessage((object)($"[SKILLKIT] cast-sync: '{spec.Label}' '{type}'/{modifier} " + $"stamped onto {num} object(s) (learned instances seen: {num2})."));
			}
		}
	}

	private static bool Apply(Item item, SpellCastType type, SpellCastModifier modifier)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_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)
		//IL_0025: 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_002c: 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)
		if (item.m_activateEffectAnimType == type && item.CastModifier == modifier && item.AlternateAnimHasSkillID == -1)
		{
			return false;
		}
		item.m_activateEffectAnimType = type;
		item.CastModifier = modifier;
		item.AlternateAnimHasSkillID = -1;
		return true;
	}
}
public static class CastGuard
{
	public static void ClearSoon(Character player)
	{
		if (!((Object)(object)player == (Object)null) && !((Object)(object)SkillRegistry.CoroutineHost == (Object)null))
		{
			SkillRegistry.CoroutineHost.StartCoroutine(ClearAfter(player));
		}
	}

	private static IEnumerator ClearAfter(Character player)
	{
		yield return null;
		yield return null;
		if ((Object)(object)player != (Object)null && player.IsCasting)
		{
			ManualLogSource log = SkillRegistry.Log;
			if (log != null)
			{
				log.LogMessage((object)"[SKILLKIT] self-clearing a lingering IsCasting after a kit skill (bug-16 self-heal).");
			}
			player.SetCastData((SpellCastType)(-1), (GameObject)null);
		}
	}
}
public class DelegateEffect : Effect
{
	public override void ActivateLocally(Character _affectedCharacter, object[] _infos)
	{
		Item val = (((Object)(object)((Effect)this).ParentItem != (Object)null) ? ((Effect)this).ParentItem : ((Component)this).GetComponentInParent<Item>());
		SkillSpec skillSpec = (((Object)(object)val != (Object)null) ? SkillRegistry.Find(val.ItemID) : null);
		if (skillSpec == null)
		{
			ManualLogSource log = SkillRegistry.Log;
			if (log != null)
			{
				log.LogWarning((object)("[SKILLKIT] DelegateEffect fired on '" + ((Object)this).name + "' but no registered spec matches ItemID=" + (((Object)(object)val != (Object)null) ? val.ItemID.ToString() : "<no parent Item>") + " — cast does nothing."));
			}
			return;
		}
		try
		{
			if (skillSpec.OnCastResult != null)
			{
				CastResult castResult = skillSpec.OnCastResult(_affectedCharacter);
				if (castResult == CastResult.Refund)
				{
					SkillCooldowns.RefundNextFrame(skillSpec.ItemId, _affectedCharacter, "[" + skillSpec.Label + "]", "refused");
				}
			}
			else
			{
				skillSpec.OnCast?.Invoke(_affectedCharacter);
			}
		}
		finally
		{
			if (skillSpec.ClearCastAfter)
			{
				CastGuard.ClearSoon(_affectedCharacter);
			}
		}
	}
}
internal static class DynamicIconEngine
{
	private static readonly Dictionary<SkillSpec, Dictionary<string, Sprite>> _cache = new Dictionary<SkillSpec, Dictionary<string, Sprite>>();

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

	internal static void InvalidateLinks()
	{
		_slLinked.Clear();
	}

	internal static void Sync(SkillSpec spec, Character player)
	{
		if (spec.Icon?.Select == null)
		{
			return;
		}
		string text = spec.Icon.Select();
		if (text == null)
		{
			return;
		}
		Sprite icon = Resolve(spec, text);
		if ((Object)(object)icon == (Object)null)
		{
			return;
		}
		RegisterStaticIconLink(spec, icon);
		var (num, num2) = SkillInstanceSync.Sync(spec.ItemId, player, (Item item) => Apply(item, icon));
		if (num > 0)
		{
			ManualLogSource log = SkillRegistry.Log;
			if (log != null)
			{
				log.LogMessage((object)("[SKILLKIT] icon sync: '" + spec.Label + "' state '" + text.ToUpperInvariant() + "' " + $"stamped onto {num} object(s) (learned instances seen: {num2})."));
			}
		}
	}

	private static bool Apply(Item item, Sprite icon)
	{
		bool flag = Converge.To<Sprite>((Func<Sprite>)(() => item.m_itemIcon), icon, (Action<Sprite>)delegate(Sprite v)
		{
			item.m_itemIcon = v;
		}, (Func<Sprite, Sprite, bool>)((Sprite a, Sprite b) => (Object)(object)a == (Object)(object)b));
		flag |= Converge.To<bool>((Func<bool>)(() => item.HasDynamicQuickSlotIcon), true, (Action<bool>)delegate(bool v)
		{
			item.HasDynamicQuickSlotIcon = v;
		}, (Func<bool, bool, bool>)null);
		Skill skill = default(Skill);
		ref Skill reference = ref skill;
		Item obj = item;
		reference = (Skill)(object)((obj is Skill) ? obj : null);
		if (skill != null)
		{
			flag |= Converge.To<Sprite>((Func<Sprite>)(() => skill.SkillTreeIcon), icon, (Action<Sprite>)delegate(Sprite v)
			{
				skill.SkillTreeIcon = v;
			}, (Func<Sprite, Sprite, bool>)((Sprite a, Sprite b) => (Object)(object)a == (Object)(object)b));
		}
		return flag;
	}

	private static void RegisterStaticIconLink(SkillSpec spec, Sprite icon)
	{
		if (spec.Icon.Sprites.Count > 1 || !_slLinked.Add(spec.ItemId))
		{
			return;
		}
		ResourcesPrefabManager instance = ResourcesPrefabManager.Instance;
		Item val = ((instance != null) ? instance.GetItemPrefab(spec.ItemId) : null);
		if ((Object)(object)val == (Object)null)
		{
			_slLinked.Remove(spec.ItemId);
			return;
		}
		try
		{
			CustomItemVisuals.SetSpriteLink(val, icon, false);
			ManualLogSource log = SkillRegistry.Log;
			if (log != null)
			{
				log.LogMessage((object)($"[SKILLKIT] icon link: '{spec.Label}' (ItemID={spec.ItemId}) " + "registered its static icon with SideLoader — learn toast + fresh clones now resolve it."));
			}
		}
		catch (Exception ex)
		{
			_slLinked.Remove(spec.ItemId);
			ManualLogSource log2 = SkillRegistry.Log;
			if (log2 != null)
			{
				log2.LogWarning((object)("[SKILLKIT] '" + spec.Label + "' SideLoader icon-link failed (will retry): " + ex.Message));
			}
		}
	}

	private static Sprite Resolve(SkillSpec spec, string key)
	{
		if (!_cache.TryGetValue(spec, out var value))
		{
			value = (_cache[spec] = new Dictionary<string, Sprite>(StringComparer.OrdinalIgnoreCase));
		}
		if (value.TryGetValue(key, out var value2))
		{
			return value2;
		}
		Sprite val = null;
		if (spec.Icon.Sprites.TryGetValue(key, out var value3))
		{
			val = Load(spec, value3);
		}
		else
		{
			ManualLogSource log = SkillRegistry.Log;
			if (log != null)
			{
				log.LogWarning((object)("[SKILLKIT] '" + spec.Label + "' icon state '" + key + "' has no sprite mapping — keeping the current icon."));
			}
		}
		value[key] = val;
		return val;
	}

	private static Sprite Load(SkillSpec spec, string name)
	{
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Expected O, but got Unknown
		//IL_015d: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			Assembly assembly = spec.ResourceAssembly ?? Assembly.GetExecutingAssembly();
			string text = null;
			string[] manifestResourceNames = assembly.GetManifestResourceNames();
			foreach (string text2 in manifestResourceNames)
			{
				if (text2.EndsWith(name, StringComparison.OrdinalIgnoreCase))
				{
					text = text2;
					break;
				}
			}
			if (text == null)
			{
				ManualLogSource log = SkillRegistry.Log;
				if (log != null)
				{
					log.LogWarning((object)("[SKILLKIT] '" + spec.Label + "' embedded icon '" + name + "' missing from " + assembly.GetName().Name + " — keeping the current icon."));
				}
				return null;
			}
			byte[] array;
			using (Stream stream = assembly.GetManifestResourceStream(text))
			{
				using MemoryStream memoryStream = new MemoryStream();
				stream.CopyTo(memoryStream);
				array = memoryStream.ToArray();
			}
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			if (!ImageConversion.LoadImage(val, array))
			{
				ManualLogSource log2 = SkillRegistry.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("[SKILLKIT] '" + spec.Label + "' icon '" + name + "' failed to decode."));
				}
				return null;
			}
			IconPin.Pin(val);
			return IconPin.Pin(Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f));
		}
		catch (Exception ex)
		{
			ManualLogSource log3 = SkillRegistry.Log;
			if (log3 != null)
			{
				log3.LogWarning((object)("[SKILLKIT] '" + spec.Label + "' icon '" + name + "' load failed: " + ex.Message));
			}
			return null;
		}
	}
}
public static class IconPin
{
	public static Texture2D Pin(Texture2D tex)
	{
		if ((Object)(object)tex != (Object)null)
		{
			((Object)tex).hideFlags = (HideFlags)61;
		}
		return tex;
	}

	public static Sprite Pin(Sprite sprite)
	{
		if ((Object)(object)sprite != (Object)null)
		{
			((Object)sprite).hideFlags = (HideFlags)61;
		}
		return sprite;
	}
}
[BepInPlugin("cobalt.skillkit", "SkillKit", "0.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
	public const string GUID = "cobalt.skillkit";

	public const string NAME = "SkillKit";

	public const string VERSION = "0.1.0";

	internal static ManualLogSource Log;

	private const float IconTickSeconds = 2f;

	private float _iconLast;

	internal void Awake()
	{
		Log = ((BaseUnityPlugin)this).Logger;
		SkillRegistry.Init(Log, (MonoBehaviour)(object)this);
		Log.LogMessage((object)"SkillKit 0.1.0 loaded.");
		Log.LogMessage((object)("[SKILLKIT] build " + BuildStamp.Read(((object)this).GetType().Assembly) + " @ " + ((object)this).GetType().Assembly.Location));
	}

	internal void Update()
	{
		CharacterManager instance = CharacterManager.Instance;
		Character player = ((instance != null) ? instance.GetFirstLocalCharacter() : null);
		SkillRegistry.SyncCastAnims(player);
		if (Time.unscaledTime - _iconLast >= 2f)
		{
			_iconLast = Time.unscaledTime;
			CastAnimEngine.InvalidateConverged();
			SkillRegistry.SyncIcons(player);
			SkillRegistry.SyncCooldowns(player);
		}
	}
}
public static class SkillClock
{
	public static float Remaining(Skill skill)
	{
		if (!((Object)(object)skill == (Object)null))
		{
			return Mathf.Max(0f, skill.m_remainingCooldownTime);
		}
		return 0f;
	}

	public static void SetRemaining(Skill skill, float seconds)
	{
		if (!((Object)(object)skill == (Object)null))
		{
			skill.m_remainingCooldownTime = Mathf.Max(0f, seconds);
			skill.m_lastUpdateCooldownTime = -999f;
			skill.m_lastReceivedCooldownProgress = -1f;
			skill.m_cooldownModifierOnCast = 1f;
			if (seconds <= 0f)
			{
				skill.m_inProgress = false;
			}
		}
	}
}
public static class SkillCooldowns
{
	public static int Sync(int itemId, Character player, float want)
	{
		want = Mathf.Max(0f, want);
		return SkillInstanceSync.Sync(itemId, player, delegate(Item item)
		{
			Skill val = (Skill)(object)((item is Skill) ? item : null);
			if (val == null || Mathf.Approximately(val.Cooldown, want))
			{
				return false;
			}
			val.Cooldown = want;
			return true;
		}).changed;
	}

	public static void RefundNextFrame(int itemId, Character player, string tag, string reason)
	{
		MonoBehaviour coroutineHost = SkillRegistry.CoroutineHost;
		if (!((Object)(object)coroutineHost == (Object)null))
		{
			coroutineHost.StartCoroutine(Refund(itemId, player, tag, reason));
		}
	}

	private static IEnumerator Refund(int itemId, Character player, string tag, string reason)
	{
		yield return null;
		int item = SkillInstanceSync.Sync(itemId, player, delegate(Item val2)
		{
			Skill val = (Skill)(object)((val2 is Skill) ? val2 : null);
			if (val == null || !val.InCooldown())
			{
				return false;
			}
			SkillClock.SetRemaining(val, 0f);
			return true;
		}).changed;
		ManualLogSource log = SkillRegistry.Log;
		if (log != null)
		{
			log.LogMessage((object)((item > 0) ? (tag + " " + reason + " — cooldown refunded.") : (tag + " " + reason + " — no running cooldown to refund (dev-verb path).")));
		}
	}
}
public static class SkillInstanceSync
{
	public delegate bool Apply(Item item);

	private static readonly Dictionary<int, Item> _prefabCache = new Dictionary<int, Item>();

	internal static void InvalidatePrefabCache()
	{
		_prefabCache.Clear();
	}

	private static Item ResolvePrefab(int itemId)
	{
		if (_prefabCache.TryGetValue(itemId, out var value) && (Object)(object)value != (Object)null)
		{
			return value;
		}
		ResourcesPrefabManager instance = ResourcesPrefabManager.Instance;
		Item val = ((instance != null) ? instance.GetItemPrefab(itemId) : null);
		if ((Object)(object)val != (Object)null)
		{
			_prefabCache[itemId] = val;
		}
		return val;
	}

	public static (int changed, int instances) Sync(int itemId, Character player, Apply apply)
	{
		int num = 0;
		int num2 = 0;
		Item val = ResolvePrefab(itemId);
		if ((Object)(object)val != (Object)null && apply(val))
		{
			num++;
		}
		try
		{
			if ((Object)(object)player != (Object)null)
			{
				CharacterInventory inventory = player.Inventory;
				object obj;
				if (inventory == null)
				{
					obj = null;
				}
				else
				{
					CharacterSkillKnowledge skillKnowledge = inventory.SkillKnowledge;
					obj = ((skillKnowledge != null) ? ((CharacterKnowledge)skillKnowledge).GetLearnedItems() : null);
				}
				IList<Item> list = (IList<Item>)obj;
				if (list != null)
				{
					for (int i = 0; i < list.Count; i++)
					{
						if ((Object)(object)list[i] != (Object)null && list[i].ItemID == itemId)
						{
							num2++;
							if (apply(list[i]))
							{
								num++;
							}
						}
					}
				}
				QuickSlot[] array = player.QuickSlotMngr?.m_quickSlots;
				if (array != null)
				{
					for (int j = 0; j < array.Length; j++)
					{
						Item val2 = (((Object)(object)array[j] != (Object)null) ? array[j].ActiveItem : null);
						if (!((Object)(object)val2 == (Object)null) && val2.ItemID == itemId)
						{
							num2++;
							if (apply(val2))
							{
								num++;
							}
						}
					}
				}
			}
		}
		catch (Exception)
		{
		}
		return (changed: num, instances: num2);
	}
}
public static class SkillRegistry
{
	public const string ActivationEffectsHostName = "ActivationEffects";

	internal static ManualLogSource Log;

	internal static MonoBehaviour CoroutineHost;

	private static readonly List<SkillSpec> _specs = new List<SkillSpec>();

	private static bool _wired;

	private static readonly HashSet<string> _warnedOnce = new HashSet<string>();

	private static string _wiredBy;

	private static readonly object PlayerWaitKey = new object();

	private static void WarnOnce(string key, string message)
	{
		if (_warnedOnce.Add(key))
		{
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogError((object)message);
			}
		}
	}

	public static void Init(ManualLogSource log, MonoBehaviour coroutineHost)
	{
		if (_wired)
		{
			if (log != null)
			{
				log.LogWarning((object)("[SKILLKIT] Init ignored — already wired by " + _wiredBy + "; log/host args discarded (the first caller owns the wiring for the process lifetime, so this mod's kit lines will appear under that one)."));
			}
			return;
		}
		_wired = true;
		_wiredBy = ((log != null) ? log.SourceName : null) ?? (((Object)(object)coroutineHost != (Object)null) ? ((object)coroutineHost).GetType().FullName : "<unknown>");
		Log = log;
		CoroutineHost = coroutineHost;
		SL.OnPacksLoaded += SetupAll;
		SceneManager.sceneLoaded += OnSceneLoaded;
	}

	[MethodImpl(MethodImplOptions.NoInlining)]
	public static void Register(SkillSpec spec)
	{
		if (spec == null || spec.ItemId == 0 || (spec.OnCast == null && spec.OnCastResult == null && !spec.Passive))
		{
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogWarning((object)"[SKILLKIT] Register: spec needs at least ItemId + OnCast/OnCastResult (or Passive=true) — ignored.");
			}
			return;
		}
		if (spec != null && spec.OnCast != null && spec.OnCastResult != null)
		{
			ManualLogSource log2 = Log;
			if (log2 != null)
			{
				log2.LogWarning((object)("[SKILLKIT] Register: '" + (spec.Label ?? spec.ItemId.ToString()) + "' sets BOTH OnCast and OnCastResult — OnCastResult wins; the plain OnCast will never run."));
			}
		}
		if (!IdPool.ClaimShared(spec.ItemId, "skill '" + (spec.Label ?? spec.ItemId.ToString()) + "'", (Action<string>)delegate(string m)
		{
			ManualLogSource log5 = Log;
			if (log5 != null)
			{
				log5.LogWarning((object)m);
			}
		}))
		{
			return;
		}
		if (Find(spec.ItemId) != null)
		{
			ManualLogSource log3 = Log;
			if (log3 != null)
			{
				log3.LogWarning((object)$"[SKILLKIT] Register: ItemID={spec.ItemId} ('{spec.Label}') already registered — ignored.");
			}
			return;
		}
		if (spec.Label == null)
		{
			spec.Label = spec.ItemId.ToString();
		}
		if (spec.Passive && (spec.OnCast != null || spec.OnCastResult != null || spec.CastAnim != null))
		{
			ManualLogSource log4 = Log;
			if (log4 != null)
			{
				log4.LogWarning((object)("[SKILLKIT] Register: '" + spec.Label + "' is Passive but sets OnCast/OnCastResult/CastAnim — a passive never activates; all nulled."));
			}
			spec.OnCast = null;
			spec.OnCastResult = null;
			spec.CastAnim = null;
		}
		if (spec.ResourceAssembly == null)
		{
			Assembly assembly = spec.OnCast?.Method.DeclaringType?.Assembly ?? spec.OnCastResult?.Method.DeclaringType?.Assembly ?? spec.Icon?.Select?.Method.DeclaringType?.Assembly;
			if (assembly == null || assembly == typeof(SkillRegistry).Assembly)
			{
				assembly = Assembly.GetCallingAssembly();
			}
			spec.ResourceAssembly = assembly;
		}
		_specs.Add(spec);
	}

	public static SkillSpec Find(int itemId)
	{
		for (int i = 0; i < _specs.Count; i++)
		{
			if (_specs[i].ItemId == itemId)
			{
				return _specs[i];
			}
		}
		return null;
	}

	private static void SetupAll()
	{
		SkillInstanceSync.InvalidatePrefabCache();
		CastAnimEngine.InvalidateConverged();
		DynamicIconEngine.InvalidateLinks();
		foreach (SkillSpec spec in _specs)
		{
			try
			{
				SetupOne(spec);
			}
			catch (Exception arg)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogError((object)($"[SKILLKIT] SetupOne '{spec.Label}' (ItemID={spec.ItemId}) threw — " + $"the remaining skills still wire: {arg}"));
				}
			}
		}
		SyncAll(null);
		ReportBootResolution();
	}

	private static void ReportBootResolution()
	{
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		foreach (SkillSpec spec in _specs)
		{
			ResourcesPrefabManager instance = ResourcesPrefabManager.Instance;
			Item val = ((instance != null) ? instance.GetItemPrefab(spec.ItemId) : null);
			Skill val2 = (Skill)(object)((val is Skill) ? val : null);
			if (val2 != null)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogMessage((object)($"[SKILLKIT] boot-resolve: '{spec.Label}' ItemID={spec.ItemId} -> Skill '{((Item)val2).Name}' " + "type=" + ((object)val2).GetType().Name + " " + $"SaveType={((Item)val2).SaveType} IsPrefab={((Item)val2).IsPrefab} HasDynamicIcon={((Item)val2).HasDynamicQuickSlotIcon}."));
				}
			}
			else
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogError((object)($"[SKILLKIT] boot-resolve: '{spec.Label}' ItemID={spec.ItemId} UNRESOLVED " + "(prefab=" + (((Object)(object)val == (Object)null) ? "null" : ((object)val).GetType().Name) + ") — the SL pack did not register this skill; a saved learn of it will be DROPPED on load. Check for a SideLoader parse error above."));
				}
			}
		}
	}

	private static void SetupOne(SkillSpec spec)
	{
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Expected O, but got Unknown
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0133: Expected O, but got Unknown
		Item itemPrefab = ResourcesPrefabManager.Instance.GetItemPrefab(spec.ItemId);
		Skill val = (Skill)(object)((itemPrefab is Skill) ? itemPrefab : null);
		if (val == null)
		{
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogWarning((object)($"[SKILLKIT] '{spec.Label}' (ItemID={spec.ItemId}) not found or not a Skill — " + "did the consumer's SideLoader pack XML fail to load? Check the log above for a SideLoader parse error."));
			}
			return;
		}
		if (spec.Passive && !(val is PassiveSkill))
		{
			ManualLogSource log2 = Log;
			if (log2 != null)
			{
				log2.LogError((object)($"[SKILLKIT] '{spec.Label}' (ItemID={spec.ItemId}) donor is {((object)val).GetType().Name}, " + "not PassiveSkill — pick a plain stat passive like Fitness 8205040; skill left un-stripped and inert."));
			}
			return;
		}
		List<GameObject> list = new List<GameObject>();
		foreach (Transform item in ((Component)val).transform)
		{
			Transform val2 = item;
			list.Add(((Component)val2).gameObject);
		}
		foreach (GameObject item2 in list)
		{
			Object.DestroyImmediate((Object)(object)item2);
		}
		if (!spec.Passive)
		{
			GameObject val3 = new GameObject("ActivationEffects");
			val3.transform.parent = ((Component)val).transform;
			val3.AddComponent<DelegateEffect>();
		}
		if ((Object)(object)((Item)val).ItemIcon != (Object)null)
		{
			val.SkillTreeIcon = ((Item)val).ItemIcon;
		}
		ManualLogSource log3 = Log;
		if (log3 != null)
		{
			log3.LogMessage((object)string.Format("[SKILLKIT] '{0}' (ItemID={1}) wired{2}.", spec.Label, spec.ItemId, spec.Passive ? " (passive)" : ""));
		}
	}

	public static void SyncCastAnims(Character player)
	{
		for (int i = 0; i < _specs.Count; i++)
		{
			try
			{
				CastAnimEngine.Sync(_specs[i], player);
			}
			catch (Exception arg)
			{
				WarnOnce("cast:" + _specs[i].Label, $"[SKILLKIT] cast-sync '{_specs[i].Label}' threw (warn-once): {arg}");
			}
		}
	}

	public static void SyncIcons(Character player)
	{
		for (int i = 0; i < _specs.Count; i++)
		{
			try
			{
				DynamicIconEngine.Sync(_specs[i], player);
			}
			catch (Exception arg)
			{
				WarnOnce("icon:" + _specs[i].Label, $"[SKILLKIT] icon-sync '{_specs[i].Label}' threw (warn-once): {arg}");
			}
		}
	}

	public static void SyncCooldowns(Character player)
	{
		for (int i = 0; i < _specs.Count; i++)
		{
			SkillSpec skillSpec = _specs[i];
			if (skillSpec.CooldownSeconds == null)
			{
				continue;
			}
			try
			{
				float num = skillSpec.CooldownSeconds();
				int num2 = SkillCooldowns.Sync(skillSpec.ItemId, player, num);
				if (num2 > 0)
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogMessage((object)$"[SKILLKIT] '{skillSpec.Label}' cooldown stamped {num:F0}s on {num2} object(s).");
					}
				}
			}
			catch (Exception arg)
			{
				WarnOnce("cd:" + skillSpec.Label, $"[SKILLKIT] cooldown-sync '{skillSpec.Label}' threw (warn-once): {arg}");
			}
		}
	}

	public static void SyncAll(Character player)
	{
		SyncCastAnims(player);
		SyncIcons(player);
		SyncCooldowns(player);
	}

	private static void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	{
		if (!((Object)(object)CoroutineHost == (Object)null) && _specs.Count != 0)
		{
			CoroutineHost.StartCoroutine(Lifecycle.WhenPlayerReady((Func<Character>)delegate
			{
				CharacterManager instance = CharacterManager.Instance;
				return (instance == null) ? null : instance.GetFirstLocalCharacter();
			}, (Action<Character>)delegate(Character player)
			{
				CastAnimEngine.InvalidateConverged();
				SyncAll(player);
				ReportLearnedState(player);
			}, (Action<string>)null, 30f, PlayerWaitKey));
		}
	}

	private static void ReportLearnedState(Character player)
	{
		object obj;
		if (player == null)
		{
			obj = null;
		}
		else
		{
			CharacterInventory inventory = player.Inventory;
			obj = ((inventory != null) ? inventory.SkillKnowledge : null);
		}
		CharacterSkillKnowledge val = (CharacterSkillKnowledge)obj;
		if ((Object)(object)val == (Object)null || _specs.Count == 0)
		{
			return;
		}
		int num = 0;
		foreach (SkillSpec spec in _specs)
		{
			if (((CharacterKnowledge)val).IsItemLearned(spec.ItemId))
			{
				num++;
				continue;
			}
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogMessage((object)($"[SKILLKIT] post-load: '{spec.Label}' ItemID={spec.ItemId} NOT in SkillKnowledge " + "(never learned, or a saved learn failed to restore)."));
			}
		}
		ManualLogSource log2 = Log;
		if (log2 != null)
		{
			log2.LogMessage((object)($"[SKILLKIT] post-load: {num}/{_specs.Count} registered skills present in " + "the player's SkillKnowledge (a saved learn that resolved at boot-resolve but is absent here = a restore/write gap)."));
		}
	}

	public static bool Learn(Character player, int itemId)
	{
		if ((Object)(object)player == (Object)null)
		{
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogWarning((object)"[SKILLKIT] Learn: no player.");
			}
			return false;
		}
		string arg = Find(itemId)?.Label ?? itemId.ToString();
		if (((CharacterKnowledge)player.Inventory.SkillKnowledge).IsItemLearned(itemId))
		{
			ManualLogSource log2 = Log;
			if (log2 != null)
			{
				log2.LogMessage((object)$"[SKILLKIT] '{arg}' (ItemID={itemId}) already learned.");
			}
			return true;
		}
		Item obj = ItemManager.Instance.GenerateItemNetwork(itemId);
		Skill val = (Skill)(object)((obj is Skill) ? obj : null);
		if (val == null)
		{
			ManualLogSource log3 = Log;
			if (log3 != null)
			{
				log3.LogWarning((object)$"[SKILLKIT] Learn: ItemID={itemId} didn't resolve to a Skill — did the SideLoader pack load?");
			}
			return false;
		}
		player.Inventory.TryUnlockSkill(val);
		ManualLogSource log4 = Log;
		if (log4 != null)
		{
			log4.LogMessage((object)$"[SKILLKIT] learned '{((Item)val).Name}' (ItemID={itemId}).");
		}
		CastAnimEngine.InvalidateConverged();
		SyncAll(player);
		return true;
	}

	public static bool Verify(Character player)
	{
		bool result = _specs.Count > 0;
		if (_specs.Count == 0)
		{
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogWarning((object)"[SKILLKIT] verify: no specs registered.");
			}
		}
		foreach (SkillSpec spec in _specs)
		{
			List<string> list = new List<string>();
			ResourcesPrefabManager instance = ResourcesPrefabManager.Instance;
			Item val = ((instance != null) ? instance.GetItemPrefab(spec.ItemId) : null);
			Skill val2 = (Skill)(object)((val is Skill) ? val : null);
			if ((Object)(object)val2 == (Object)null)
			{
				list.Add("prefab missing or not a Skill");
			}
			else if (spec.Passive)
			{
				if (!(val2 is PassiveSkill))
				{
					list.Add("prefab is " + ((object)val2).GetType().Name + ", not PassiveSkill (bad donor — SetupOne left it un-stripped and inert)");
				}
			}
			else
			{
				Transform val3 = ((Component)val2).transform.Find("ActivationEffects");
				if ((Object)(object)val3 == (Object)null)
				{
					list.Add("no 'ActivationEffects' child (pack-load setup didn't run?)");
				}
				else if ((Object)(object)((Component)val3).GetComponent<DelegateEffect>() == (Object)null)
				{
					list.Add("effect host has no DelegateEffect");
				}
			}
			if (spec.Icon != null)
			{
				if (spec.Icon.Select == null)
				{
					list.Add("Icon.Select is null");
				}
				foreach (KeyValuePair<string, string> sprite in spec.Icon.Sprites)
				{
					if (string.IsNullOrEmpty(sprite.Value))
					{
						list.Add("icon state '" + sprite.Key + "' maps to an empty resource name");
					}
				}
				if ((Object)(object)val2 != (Object)null && !((Item)val2).HasDynamicQuickSlotIcon)
				{
					list.Add("prefab HasDynamicQuickSlotIcon is false (icon sync never ran?)");
				}
			}
			if ((Object)(object)player != (Object)null)
			{
				CharacterInventory inventory = player.Inventory;
				if ((Object)(object)((inventory != null) ? inventory.SkillKnowledge : null) != (Object)null && !((CharacterKnowledge)player.Inventory.SkillKnowledge).IsItemLearned(spec.ItemId))
				{
					ManualLogSource log2 = Log;
					if (log2 != null)
					{
						log2.LogMessage((object)("[SKILLKIT] verify note: '" + spec.Label + "' not learned by the current player (fine — prefab checks only)."));
					}
				}
			}
			if (list.Count == 0)
			{
				ManualLogSource log3 = Log;
				if (log3 != null)
				{
					log3.LogMessage((object)$"[SKILLKIT] verify PASS: '{spec.Label}' (ItemID={spec.ItemId}).");
				}
				continue;
			}
			result = false;
			ManualLogSource log4 = Log;
			if (log4 != null)
			{
				log4.LogWarning((object)string.Format("[SKILLKIT] verify FAIL: '{0}' (ItemID={1}): {2}.", spec.Label, spec.ItemId, string.Join("; ", list)));
			}
		}
		return result;
	}

	public static void DumpLearned(Character player, string filter)
	{
		//IL_008d: 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 ((Object)(object)player == (Object)null)
		{
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogWarning((object)"[SKILLKIT] dump: no player.");
			}
			return;
		}
		IList<Item> learnedItems = ((CharacterKnowledge)player.Inventory.SkillKnowledge).GetLearnedItems();
		int num = 0;
		foreach (Item item in learnedItems)
		{
			if (item is Skill && item.Name != null && (filter == null || item.Name.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0))
			{
				num++;
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogMessage((object)($"[SKILLKIT] learned: '{item.Name}' (ItemID={item.ItemID}) ActivateEffectAnimType={item.ActivateEffectAnimType} " + $"CastModifier={item.CastModifier} AlternateAnimHasSkillID={item.AlternateAnimHasSkillID}"));
				}
			}
		}
		if (num == 0)
		{
			ManualLogSource log3 = Log;
			if (log3 != null)
			{
				log3.LogMessage((object)("[SKILLKIT] no learned skill matching '" + filter + "'."));
			}
		}
	}

	public static void DumpPrefabs(string filter)
	{
		int num = 0;
		foreach (Item value in ResourcesPrefabManager.ITEM_PREFABS.Values)
		{
			if (value is Skill && value.Name != null && (filter == null || value.Name.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0))
			{
				num++;
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogMessage((object)$"[SKILLKIT] prefab: '{value.Name}' (ItemID={value.ItemID}) type={((object)value).GetType().Name}");
				}
			}
		}
		ManualLogSource log2 = Log;
		if (log2 != null)
		{
			log2.LogMessage((object)$"[SKILLKIT] {num} prefab match(es).");
		}
	}

	public static void CastDump(Character player)
	{
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null)
		{
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogWarning((object)"[SKILLKIT] castdump: no player.");
			}
			return;
		}
		GameObject castReceiver = player.CastReceiver;
		ManualLogSource log2 = Log;
		if (log2 != null)
		{
			log2.LogMessage((object)($"[SKILLKIT] cast state: IsCasting={player.IsCasting} CurrentSpellCast={player.CurrentSpellCast} " + "CastReceiver=" + (((Object)(object)castReceiver != (Object)null) ? ((Object)castReceiver).name : "<null>") + " " + $"InLocomotion={player.InLocomotion} NextIsLocomotion={player.NextIsLocomotion}"));
		}
	}

	public static void CastClear(Character player)
	{
		if ((Object)(object)player == (Object)null)
		{
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogWarning((object)"[SKILLKIT] castclear: no player.");
			}
			return;
		}
		bool isCasting = player.IsCasting;
		player.SetCastData((SpellCastType)(-1), (GameObject)null);
		ManualLogSource log2 = Log;
		if (log2 != null)
		{
			log2.LogMessage((object)$"[SKILLKIT] SetCastData(NONE, null) — was IsCasting={isCasting}, now IsCasting={player.IsCasting}.");
		}
	}
}
public sealed class SkillSpec
{
	public int ItemId;

	public string Label;

	public Action<Character> OnCast;

	public Func<Character, CastResult> OnCastResult;

	public Func<float> CooldownSeconds;

	public bool Passive;

	public bool ClearCastAfter = true;

	public DynamicIcon Icon;

	public Func<Character, CastPick> CastAnim;

	public Assembly ResourceAssembly;
}
public enum CastResult
{
	Landed,
	Refund,
	RefuseSilently
}
public sealed class DynamicIcon
{
	public Func<string> Select;

	public Dictionary<string, string> Sprites = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

	public static DynamicIcon Fixed(string spriteName)
	{
		return new DynamicIcon
		{
			Select = () => "fixed",
			Sprites = { ["fixed"] = spriteName }
		};
	}
}
public struct CastPick
{
	public string AnimName;

	public SpellCastModifier Modifier;

	public CastPick(string animName, SpellCastModifier modifier)
	{
		//IL_0008: 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)
		AnimName = animName;
		Modifier = modifier;
	}
}
public static class SkillVerbs
{
	public static void RegisterAll(CommandRegistry c, ManualLogSource log, Func<Character> player)
	{
		c.Register("castdump", "Dump the player's stuck-cast state (bug-16 observability).", (Action<string[]>)delegate
		{
			SkillRegistry.CastDump(player());
		});
		c.Register("castclear", "Clear a wedged IsCasting (bug-16 recovery).", (Action<string[]>)delegate
		{
			SkillRegistry.CastClear(player());
		});
		c.Register("skillverify", "SkillKit wire-check: prefab/effect/icon per registered spec.", (Action<string[]>)delegate
		{
			SkillRegistry.Verify(player());
		});
		c.Register("skilldump", "Dump learned skills whose name contains a substring ('skilldump <name>').", (Action<string[]>)delegate(string[] parts)
		{
			if (parts.Length < 2)
			{
				log.LogWarning((object)"[SKILLDUMP] usage: skilldump <name-substring>");
			}
			else
			{
				SkillRegistry.DumpLearned(player(), Tail(parts));
			}
		});
		c.Register("skillitemdump", "Browse loadable Skill-type item prefabs ('skillitemdump [name]').", (Action<string[]>)delegate(string[] parts)
		{
			SkillRegistry.DumpPrefabs(Tail(parts));
		});
	}

	private static string Tail(string[] parts)
	{
		if (parts == null || parts.Length <= 1)
		{
			return null;
		}
		return string.Join(" ", parts, 1, parts.Length - 1);
	}
}
public static class SlStatus
{
	public static readonly string[] DefaultDonors = new string[3] { "Bleeding", "Burning", "Poisoned" };

	public static ManualLogSource Log;

	public static Func<string, string, Sprite> DefaultIconLoader;

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

	private static readonly Dictionary<string, Func<string, string, Sprite>> _iconLoader = new Dictionary<string, Func<string, string, Sprite>>();

	private static readonly Dictionary<string, Sprite> _iconCache = new Dictionary<string, Sprite>();

	private static readonly HashSet<string> _iconDead = new HashSet<string>();

	private static ManualLogSource L => Log ?? Plugin.Log;

	public static bool ResolveDonor(SlStatusSpec spec, out string donor)
	{
		donor = Array.Find(spec.DonorCandidates, (string id) => (Object)(object)ResourcesPrefabManager.Instance.GetStatusEffectPrefab(id) != (Object)null);
		if (donor == null)
		{
			ManualLogSource l = L;
			if (l != null)
			{
				l.LogWarning((object)(spec.Tag + " no donor status found (tried " + string.Join(", ", spec.DonorCandidates) + ") — " + spec.DisabledSuffix));
			}
			return false;
		}
		return true;
	}

	public static bool Register(SlStatusSpec spec, string donor, out StatusEffect prefab)
	{
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Expected O, but got Unknown
		prefab = null;
		if (!IdPool.ClaimShared(spec.NumId, "status '" + spec.Id + "'", (Action<string>)delegate(string m)
		{
			Plugin.Log.LogWarning((object)m);
		}))
		{
			return false;
		}
		SL_StatusEffect val = new SL_StatusEffect();
		val.TargetStatusIdentifier = donor;
		val.StatusIdentifier = spec.Id;
		val.NewStatusID = spec.NumId;
		val.Name = spec.Name;
		val.Description = spec.Description;
		val.Lifespan = spec.Lifespan;
		val.Purgeable = false;
		val.DisplayedInHUD = true;
		val.IsHidden = false;
		val.IsMalusEffect = spec.IsMalus;
		val.ActionOnHit = (ActionsOnHit)0;
		val.PlayFXOnActivation = false;
		val.Tags = new string[0];
		SL_StatusEffect val2 = val;
		if (spec.BindFamily != null)
		{
			val2.FamilyMode = (FamilyModes)0;
			val2.BindFamily = spec.BindFamily;
		}
		((ContentTemplate)val2).ApplyTemplate();
		prefab = ResourcesPrefabManager.Instance.GetStatusEffectPrefab(spec.Id);
		if ((Object)(object)prefab == (Object)null)
		{
			ManualLogSource l = L;
			if (l != null)
			{
				l.LogWarning((object)(spec.Tag + " '" + spec.Id + "' missing after ApplyTemplate (see SideLoader warnings above)."));
			}
			return false;
		}
		StripInheritedFx(prefab, donor, spec.Id, spec.Tag);
		_iconPng[spec.Id] = spec.IconPng;
		_iconLoader[spec.Id] = spec.IconLoader ?? DefaultIconLoader;
		Sprite val3 = LoadIcon(spec.Id, spec.IconPng, spec.Tag);
		if ((Object)(object)val3 != (Object)null)
		{
			_iconCache[spec.Id] = val3;
			StampIcon(prefab, val3);
		}
		return true;
	}

	public static void ResetIconRetries()
	{
		if (_iconDead.Count != 0)
		{
			ManualLogSource l = L;
			if (l != null)
			{
				l.LogMessage((object)$"[ICONS] retrying {_iconDead.Count} icon(s) that failed to load earlier this session.");
			}
			_iconDead.Clear();
		}
	}

	private static Sprite LoadIcon(string id, string png, string tag)
	{
		if (!_iconLoader.TryGetValue(id, out var value))
		{
			value = DefaultIconLoader;
		}
		if (value == null)
		{
			ManualLogSource l = L;
			if (l != null)
			{
				l.LogWarning((object)(tag + " '" + id + "': no icon loader set (SkillKit.SlStatus.DefaultIconLoader) — the status will show a blank slot."));
			}
			return null;
		}
		return value(png, tag);
	}

	private static void StampIcon(StatusEffect status, Sprite icon)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Expected O, but got Unknown
		status.OverrideIcon = icon;
		status.m_defaultStatusIcon = new StatusTypeIcon(Tag.None)
		{
			Icon = icon
		};
	}

	public static bool IconHeld(string id)
	{
		ResourcesPrefabManager instance = ResourcesPrefabManager.Instance;
		StatusEffect val = ((instance != null) ? instance.GetStatusEffectPrefab(id) : null);
		if ((Object)(object)val != (Object)null)
		{
			return (Object)(object)val.OverrideIcon != (Object)null;
		}
		return false;
	}

	public static bool EnsureIcon(string id, StatusEffect live, string tag)
	{
		if (!_iconPng.TryGetValue(id, out var value))
		{
			return false;
		}
		if (_iconDead.Contains(id))
		{
			return false;
		}
		ResourcesPrefabManager instance = ResourcesPrefabManager.Instance;
		StatusEffect val = ((instance != null) ? instance.GetStatusEffectPrefab(id) : null);
		bool flag = (Object)(object)val == (Object)null || (Object)(object)val.OverrideIcon != (Object)null;
		bool flag2 = (Object)(object)live == (Object)null || (Object)(object)live.OverrideIcon != (Object)null;
		if (flag && flag2)
		{
			return false;
		}
		if (!_iconCache.TryGetValue(id, out var value2) || (Object)(object)value2 == (Object)null)
		{
			value2 = LoadIcon(id, value, tag);
			if ((Object)(object)value2 == (Object)null)
			{
				_iconDead.Add(id);
				ManualLogSource l = L;
				if (l != null)
				{
					l.LogWarning((object)(tag + " '" + id + "': embedded icon '" + value + "' could not be loaded — giving up on it for this session (it will show the blank/donor slot). Retry with `reloadcfg` after a rebuild."));
				}
				return false;
			}
			_iconCache[id] = value2;
		}
		if ((Object)(object)val != (Object)null)
		{
			StampIcon(val, value2);
		}
		if ((Object)(object)live != (Object)null)
		{
			StampIcon(live, value2);
		}
		return true;
	}

	public static string IconState(string id, StatusEffect live)
	{
		ResourcesPrefabManager instance = ResourcesPrefabManager.Instance;
		StatusEffect status = ((instance != null) ? instance.GetStatusEffectPrefab(id) : null);
		string text = id + ": prefab=" + Describe(status);
		if ((Object)(object)live != (Object)null)
		{
			text = text + ", live=" + Describe(live);
		}
		return text;
	}

	private static string Describe(StatusEffect status)
	{
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)status == (Object)null)
		{
			return "<none>";
		}
		Sprite overrideIcon = status.OverrideIcon;
		if ((Object)(object)overrideIcon == (Object)null)
		{
			return "use=False (OverrideIcon null/destroyed -> DONOR BADGE)";
		}
		return string.Format("use=True tex={0} flags={1}", ((Object)(object)overrideIcon.texture == (Object)null) ? "null" : "ok", ((Object)overrideIcon).hideFlags);
	}

	public static void StripInheritedFx(StatusEffect prefab, string donor, string id, string tag)
	{
		//IL_00d5: 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)
		if ((Object)(object)prefab == (Object)null)
		{
			return;
		}
		ResourcesPrefabManager instance = ResourcesPrefabManager.Instance;
		StatusEffect val = ((instance != null) ? instance.GetStatusEffectPrefab(donor) : null);
		if ((Object)(object)val != (Object)null && prefab == val)
		{
			ManualLogSource l = L;
			if (l != null)
			{
				l.LogWarning((object)(tag + " '" + id + "' IS the shared donor prefab '" + donor + "' — refusing to strip its FX (that would break vanilla " + donor + " for the whole game). The clone must have failed; see SideLoader warnings."));
			}
			return;
		}
		if ((Object)(object)prefab.FXPrefab != (Object)null)
		{
			ManualLogSource l2 = L;
			if (l2 != null)
			{
				l2.LogMessage((object)(tag + " '" + id + "': dropped inherited FX prefab '" + ((Object)prefab.FXPrefab).name + "' (bug 27 — an unbound zero-area emitter, one log line per frame for the status's whole life)."));
			}
		}
		prefab.FXPrefab = null;
		prefab.FxInstantiation = (FXInstantiationTypes)0;
		prefab.SpecialFXPrefab = null;
		prefab.SpecialFxInstantiation = (FXInstantiationTypes)0;
		prefab.PlaySpecialFXOnStop = false;
	}

	public static StatusEffect Live(Character player, string id)
	{
		object obj;
		if (player == null)
		{
			obj = null;
		}
		else
		{
			StatusEffectManager statusEffectMngr = player.StatusEffectMngr;
			obj = ((statusEffectMngr != null) ? statusEffectMngr.GetStatusEffectOfName(id) : null);
		}
		StatusEffect val = (StatusEffect)obj;
		if (EnsureIcon(id, val, "[ICONS]"))
		{
			ManualLogSource l = L;
			if (l != null)
			{
				l.LogMessage((object)("[ICONS] re-stamped " + id + " — its icon had gone missing (the HUD would have been showing the donor badge instead)."));
			}
		}
		return val;
	}

	public static int StackCount(Character player, string id)
	{
		StatusEffect obj = Live(player, id);
		if (obj == null)
		{
			return 0;
		}
		return obj.StackCount;
	}

	public static void AddStack(Character player, string id)
	{
		if (player != null)
		{
			StatusEffectManager statusEffectMngr = player.StatusEffectMngr;
			if (statusEffectMngr != null)
			{
				statusEffectMngr.AddStatusEffect(id);
			}
		}
	}

	public static void Remove(Character player, string id)
	{
		if (player != null)
		{
			StatusEffectManager statusEffectMngr = player.StatusEffectMngr;
			if (statusEffectMngr != null)
			{
				statusEffectMngr.RemoveStatusWithIdentifierName(id);
			}
		}
	}

	public static void RefreshAll(Character player, string id, float durationSeconds)
	{
		StatusEffect val = Live(player, id);
		if (val?.m_statusStack == null)
		{
			return;
		}
		foreach (StatusData item in val.m_statusStack)
		{
			item.LifeSpan = durationSeconds;
			item.ResetLifeSpan();
		}
	}

	public static void SetStacks(Character player, string id, int n, float durationSeconds, bool canAdd)
	{
		if ((Object)(object)((player != null) ? player.StatusEffectMngr : null) == (Object)null)
		{
			return;
		}
		if (n <= 0)
		{
			Remove(player, id);
		}
		else if (canAdd)
		{
			int num = 16;
			while (StackCount(player, id) < n && num-- > 0)
			{
				AddStack(player, id);
			}
			StatusEffect val = Live(player, id);
			while ((Object)(object)val != (Object)null && val.StackCount > n && num-- > 0)
			{
				val.RemoveOldestStack();
			}
			RefreshAll(player, id, durationSeconds);
		}
	}

	public static void AddOrRefresh(Character player, string id, float durationSeconds)
	{
		if ((Object)(object)((player != null) ? player.StatusEffectMngr : null) == (Object)null)
		{
			return;
		}
		StatusEffect val = Live(player, id);
		if ((Object)(object)val != (Object)null)
		{
			if (val.StatusData != null)
			{
				val.StatusData.LifeSpan = durationSeconds;
				val.StatusData.ResetLifeSpan();
			}
		}
		else
		{
			player.StatusEffectMngr.AddStatusEffect(id);
		}
	}

	public static void SyncDuration(string id, float durationSeconds)
	{
		ResourcesPrefabManager instance = ResourcesPrefabManager.Instance;
		StatusEffect val = ((instance != null) ? instance.GetStatusEffectPrefab(id) : null);
		if (val?.StatusData != null)
		{
			val.StatusData.LifeSpan = durationSeconds;
		}
	}
}
public class SlStatusSpec
{
	public string Id;

	public int NumId;

	public string Name;

	public string Description;

	public float Lifespan;

	public string IconPng;

	public bool IsMalus;

	public string Tag;

	public string DisabledSuffix;

	public string[] DonorCandidates = SlStatus.DefaultDonors;

	public SL_StatusEffectFamily BindFamily;

	public Func<string, string, Sprite> IconLoader;
}