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