using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using SeneaL.API.Early; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("SeneaL-API")] [assembly: AssemblyProduct("SeneaL-API")] [assembly: AssemblyCompany("seneaL")] [assembly: AssemblyCopyright("seneaL")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SeneaL.API { public static class Api { public const string Guid = "seneaL.valheim.api"; public const string Name = "SeneaL API"; public const int Contract = 2; public static readonly string[] Features = new string[16] { "notices", "dialogs", "hud", "slots", "slots.linked", "azuepi", "hud.placed", "slots.use", "slots.time", "slots.place", "slots.icon", "theme", "dialogs.choice", "theme.icons", "settings", "slots.handlers" }; public static string Version => "1.0.1"; public static bool Has(string feature) { return Array.IndexOf(Features, feature) >= 0; } } internal static class AzuBridge { private sealed class Entry { public SlotDef Def; public Func Get; public Func Valid; } public const string Guid = "Azumatt.AzuExtendedPlayerInventory"; private static readonly Dictionary Known = new Dictionary(8); private static readonly List Gone = new List(4); private static readonly HashSet Seen = new HashSet(); private static Type _bound; private static MethodInfo _slots; private static MethodInfo _desc; private static bool _warned; public static Func NameOf; public static int Count => Known.Count; public static bool Bound => _bound != null; public static bool Bind(Type api) { if (api == null) { if (_bound != null) { Clear(); } return false; } if ((object)api == _bound) { return true; } Clear(); MethodInfo method = api.GetMethod("GetSlots", BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null); MethodInfo method2 = api.GetMethod("TryGetSlotDescriptor", BindingFlags.Static | BindingFlags.Public); if (method == null || method2 == null || method2.GetParameters().Length != 2) { if (!_warned) { _warned = true; Plugin.Warn("AzuEPI is installed, but its slot API has another shape than this bridge knows; its slots are not shown."); } return false; } _slots = method; _desc = method2; _bound = api; return true; } private static string OriginOf(Delegate fn, Type api) { Assembly assembly = fn?.Method.DeclaringType?.Assembly; if (assembly == null || (api != null && assembly == api.Assembly)) { return null; } if (NameOf == null) { return assembly.GetName().Name; } return NameOf(assembly); } public static void Clear() { CustomSlots.RemoveAll("Azumatt.AzuExtendedPlayerInventory"); Known.Clear(); _bound = null; _slots = (_desc = null); } public static void Sync() { if (_bound == null) { return; } object obj; try { obj = _slots.Invoke(null, null); } catch (Exception e) { Plugin.Fail("AzuEPI GetSlots", e); return; } if (obj == null) { return; } Type type = obj.GetType(); string[] array = (string[])type.GetProperty("SlotNames")?.GetValue(obj, null); string[] array2 = (string[])type.GetProperty("OriginalSlotNames")?.GetValue(obj, null); Array array3 = (Array)(type.GetProperty("GetItemFuncs")?.GetValue(obj, null)); Array array4 = (Array)(type.GetProperty("IsValidFuncs")?.GetValue(obj, null)); if (array == null || array2 == null) { return; } Seen.Clear(); for (int i = 0; i < array.Length; i++) { object[] array5 = new object[2] { i, null }; if (!(bool)_desc.Invoke(null, array5) || array5[1] == null) { continue; } object obj2 = array5[1]; Type type2 = obj2.GetType(); if (!(bool)type2.GetProperty("IsCustom").GetValue(obj2, null) || (bool)type2.GetProperty("IsQuickSlot").GetValue(obj2, null)) { continue; } string text = (string.IsNullOrEmpty(array2[i]) ? array[i] : array2[i]); if (string.IsNullOrEmpty(text)) { continue; } Seen.Add(text); Func func = ((array3 != null && i < array3.Length) ? (array3.GetValue(i) as Func) : null); Func valid = ((array4 != null && i < array4.Length) ? (array4.GetValue(i) as Func) : null); if (!Known.TryGetValue(text, out var value)) { Entry entry; value = (entry = new Entry()); value.Def = new SlotDef(text) { Title = array[i], Origin = (SlotOrigins.Get(text) ?? SlotOrigins.Get(array[i]) ?? OriginOf(func, _bound)), Mode = SlotMode.Linked, Order = 1000 + i, Source = (Player p) => entry.Get?.Invoke(p), Accepts = (ItemData it) => entry.Valid == null || entry.Valid(it) }; value.Get = func; value.Valid = valid; Known[text] = value; CustomSlots.Add("Azumatt.AzuExtendedPlayerInventory", value.Def); } else { value.Get = func; value.Valid = valid; if (string.IsNullOrEmpty(value.Def.Origin)) { value.Def.Origin = SlotOrigins.Get(text) ?? SlotOrigins.Get(array[i]) ?? OriginOf(func, _bound); } if (value.Def.Title != array[i]) { value.Def.Title = array[i]; } } } Gone.Clear(); foreach (string key in Known.Keys) { if (!Seen.Contains(key)) { Gone.Add(key); } } for (int num = 0; num < Gone.Count; num++) { Known.Remove(Gone[num]); CustomSlots.Remove("Azumatt.AzuExtendedPlayerInventory", Gone[num]); } } } internal static class SlotOrigins { private static readonly Dictionary Names = new Dictionary(8, StringComparer.OrdinalIgnoreCase); public static Func Fallback; public static int Count => Names.Count; public static void Record(string slot, string mod) { if (!string.IsNullOrEmpty(slot) && !string.IsNullOrEmpty(mod)) { Names[slot] = mod; } } public static void Forget(string slot) { if (!string.IsNullOrEmpty(slot)) { Names.Remove(slot); } } public static string Get(string slot) { if (!string.IsNullOrEmpty(slot)) { if (!Names.TryGetValue(slot, out var value)) { return Fallback?.Invoke(slot); } return value; } return null; } public static void Clear() { Names.Clear(); } public static Assembly Pick(IList frames, Assembly own, Func skip) { for (int i = 0; i < frames.Count; i++) { Assembly assembly = frames[i]; if (!(assembly == null) && (skip == null || !skip(assembly))) { if (!(assembly == own)) { return assembly; } return null; } } return null; } } internal static class StubHost { private sealed class Emu { public SlotDef Def; public readonly List> Gets = new List>(2); public readonly List> Valids = new List>(2); } public const string Owner = "AzuEPI.emulated"; private static readonly Dictionary Emus = new Dictionary(8); private static bool _on; public static void Start() { if (!_on && AppDomain.CurrentDomain.GetAssemblies().Any((Assembly a) => a.GetName().Name == "SeneaL-API-Early")) { _on = true; Hook(); } } private static void Hook() { StubBridge.Wake = Drain; Plugin.Log.LogInfo((object)("AzuEPI API stubs embedded in other mods: " + StubBridge.Patched + " methods routed here")); Drain(); } private static void Drain() { StubCall[] array = StubBridge.Take(); foreach (StubCall val in array) { try { Handle(val); } catch (Exception e) { Plugin.Fail("AzuEPI stub " + val.Owner + "." + val.Method, e); } } } private static void Handle(StubCall c) { Plugin.Trace("AzuEPI stub " + c.Owner + "." + c.Method + " " + ((c.Args.Length != 0) ? c.Args[0] : "")); if (c.Args.Length != 0 && c.Args[0] is string slot) { if (c.Method == "AddSlot") { SlotOrigins.Record(slot, PluginName(c.Owner)); } else if (c.Method == "RemoveSlot") { SlotOrigins.Forget(slot); } } if (Chainloader.PluginInfos.TryGetValue("Azumatt.AzuExtendedPlayerInventory", out var value) && Object.op_Implicit((Object)(object)value.Instance)) { MethodInfo methodInfo = ((object)value.Instance).GetType().Assembly.GetType("AzuEPI.API")?.GetMethod(c.Method, c.Types); if (methodInfo != null) { methodInfo.Invoke(null, c.Args); return; } } if (!CustomSlots.AzuManaged) { Emulate(c); } } private static void Emulate(StubCall c) { string text = ((c.Args.Length != 0) ? (c.Args[0] as string) : null); if (string.IsNullOrWhiteSpace(text)) { return; } if (c.Method == "RemoveSlot") { Emus.Remove(text); CustomSlots.Remove("AzuEPI.emulated", text); return; } Func func = null; Func valid = null; if (c.Args.Length == 4 && c.Args[1] is Func func2) { func = func2; valid = c.Args[2] as Func; } else { if (c.Args.Length == 3) { object obj = c.Args[1]; string prefab = obj as string; if (prefab != null) { valid = (ItemData it) => it != null && Object.op_Implicit((Object)(object)it.m_dropPrefab) && ((Object)it.m_dropPrefab).name == prefab; goto IL_0170; } } if (c.Args.Length == 3 && c.Args[1] is IEnumerable source) { HashSet set = new HashSet(source.Where((string n) => !string.IsNullOrWhiteSpace(n))); valid = (ItemData it) => it != null && Object.op_Implicit((Object)(object)it.m_dropPrefab) && set.Contains(((Object)it.m_dropPrefab).name); } else if (c.Args.Length == 4 && c.Args[1] is Func func3) { valid = func3; } } goto IL_0170; IL_0170: if (func == null && valid == null) { return; } if (!Emus.TryGetValue(text, out var value)) { Emu emu; value = (emu = new Emu()); value.Def = new SlotDef(text) { Title = text, Origin = PluginName(c.Owner), Mode = SlotMode.Linked, Order = 2000 + Emus.Count, Source = (Player p) => Find(emu, p), Accepts = (ItemData it) => emu.Valids.Any((Func f) => f(it)) }; Emus[text] = value; } if (func != null) { value.Gets.Add(func); } if (valid != null) { value.Valids.Add(valid); if (func == null) { value.Gets.Add(delegate(Player p) { if (p == null) { return (ItemData)null; } Inventory inventory = ((Humanoid)p).GetInventory(); return (inventory == null) ? null : inventory.GetEquippedItems()?.FirstOrDefault(valid); }); } } CustomSlots.Add("AzuEPI.emulated", value.Def); } private static string PluginName(string asm) { foreach (PluginInfo value in Chainloader.PluginInfos.Values) { if (Object.op_Implicit((Object)(object)value.Instance) && ((object)value.Instance).GetType().Assembly.GetName().Name == asm) { return value.Metadata.Name; } } return asm; } private static ItemData Find(Emu e, Player p) { for (int i = 0; i < e.Gets.Count; i++) { ItemData val = e.Gets[i](p); if (val != null) { return val; } } return null; } } internal static class BuildInfo { public const string Channel = "release"; public const string Version = "1.0.1"; public const int Number = 0; public const string Time = "2026-10-18 12:14 UTC"; public const string Id = "0eead253"; } internal sealed class Slot where T : class { private sealed class Entry { public string Owner; public T Provider; public int Priority; public int Serial; } private readonly List _items = new List(2); private int _serial; public T Current { get; private set; } public string Owner { get; private set; } public int Count => _items.Count; public event Action Changed; public void Set(string owner, T provider, int priority) { if (string.IsNullOrEmpty(owner)) { throw new ArgumentException("owner is required", "owner"); } if (provider == null) { throw new ArgumentNullException("provider"); } Drop(owner); _items.Add(new Entry { Owner = owner, Provider = provider, Priority = priority, Serial = ++_serial }); Pick(); } public T Get(string owner) { for (int i = 0; i < _items.Count; i++) { if (_items[i].Owner == owner) { return _items[i].Provider; } } return null; } public bool Remove(string owner) { if (!Drop(owner)) { return false; } Pick(); return true; } private bool Drop(string owner) { for (int i = 0; i < _items.Count; i++) { if (!(_items[i].Owner != owner)) { _items.RemoveAt(i); return true; } } return false; } private void Pick() { Entry entry = null; for (int i = 0; i < _items.Count; i++) { Entry entry2 = _items[i]; if (entry == null || entry2.Priority > entry.Priority || (entry2.Priority == entry.Priority && entry2.Serial > entry.Serial)) { entry = entry2; } } T val = ((entry != null) ? entry.Provider : null); if (val != Current) { Current = val; Owner = entry?.Owner; this.Changed?.Invoke(val); } } } public enum DialogKind { Confirm, Message, Input, Choice } public abstract class Dialog { internal int Depth; internal string Owner; internal int Frame; internal Action Fallback; public abstract DialogKind Kind { get; } public string Title { get; set; } public string Text { get; set; } public bool Centered { get; set; } public bool IsOpen { get; internal set; } public bool Vanilla { get; internal set; } public void Close() { Dialogs.Close(this); } internal void Answer(Action callback) { if (IsOpen) { if (!Vanilla) { Dialogs.Close(this); } try { callback?.Invoke(); } catch (Exception e) { Plugin.Fail("dialog callback", e); } Dialogs.Finish(this); } } } public sealed class ConfirmDialog : Dialog { private readonly Action _yes; private readonly Action _no; public override DialogKind Kind => DialogKind.Confirm; public string YesText { get; set; } public string NoText { get; set; } public ConfirmDialog(Action yes, Action no) { _yes = yes; _no = no; } public void Yes() { Answer(_yes); } public void No() { Answer(_no); } } public sealed class MessageDialog : Dialog { private readonly Action _ok; public override DialogKind Kind => DialogKind.Message; public string OkText { get; set; } public MessageDialog(Action ok) { _ok = ok; } public void Ok() { Answer(_ok); } } public sealed class InputDialog : Dialog { private readonly Action _submit; private readonly Action _cancel; private readonly Func _validate; public override DialogKind Kind => DialogKind.Input; public string Value { get; set; } public string Placeholder { get; set; } public int CharLimit { get; set; } public InputDialog(Action submit, Action cancel, Func validate) { _submit = submit; _cancel = cancel; _validate = validate; } public bool IsValid(string value) { if (_validate != null) { return _validate(value); } return true; } public void Submit(string value) { if (base.IsOpen && IsValid(value)) { Answer(delegate { _submit?.Invoke(value); }); } } public void Cancel() { Answer(_cancel); } } public sealed class DialogButton { public string Text { get; set; } public bool Primary { get; set; } public Action Click { get; set; } public DialogButton(string text, Action click, bool primary = false) { Text = text; Click = click; Primary = primary; } } public sealed class ChoiceDialog : Dialog { private readonly DialogButton[] _buttons; public override DialogKind Kind => DialogKind.Choice; public IReadOnlyList Buttons => _buttons; public int CancelIndex { get; set; } public ChoiceDialog(DialogButton[] buttons) { _buttons = buttons ?? new DialogButton[0]; CancelIndex = _buttons.Length - 1; } public void Choose(int index) { if (index >= 0 && index < _buttons.Length) { Answer(_buttons[index].Click); } } public void Cancel() { if (CancelIndex >= 0) { Choose(CancelIndex); } else { Close(); } } } public abstract class DialogProvider { public abstract bool Show(Dialog dialog); public virtual void Hide(Dialog dialog) { } } [HarmonyPatch(typeof(UnifiedPopup), "Push")] internal static class PushPopupPatch { [HarmonyPrefix] [HarmonyPriority(0)] private static bool Pre(PopupBase popup, bool __runOriginal) { if (__runOriginal) { return !Dialogs.InterceptPush(popup); } return true; } } [HarmonyPatch(typeof(UnifiedPopup), "Pop")] internal static class PopPopupPatch { [HarmonyPrefix] private static bool Pre() { return !Dialogs.InterceptPop(); } } [HarmonyPatch(typeof(UnifiedPopup), "IsVisible")] internal static class PopupVisiblePatch { [HarmonyPostfix] private static void Post(ref bool __result) { if (!__result) { __result = Dialogs.IsOpen; } } } [HarmonyPatch(typeof(UnifiedPopup), "WasVisibleThisFrame")] internal static class PopupWasVisiblePatch { [HarmonyPostfix] private static void Post(ref bool __result) { if (!__result) { __result = Dialogs.ClosedThisFrame; } } } [HarmonyPatch(typeof(TextInput), "RequestText")] internal static class RequestTextPatch { [HarmonyPrefix] [HarmonyPriority(0)] private static bool Pre(TextReceiver sign, string topic, int charLimit, bool __runOriginal) { if (__runOriginal) { return !Dialogs.InterceptText(sign, topic, charLimit); } return true; } } [HarmonyPatch(typeof(TextInput), "IsVisible")] internal static class TextVisiblePatch { [HarmonyPostfix] private static void Post(ref bool __result) { if (!__result) { __result = Dialogs.InputOpen; } } } public static class Dialogs { private static readonly Slot Providers = new Slot(); private static readonly List Opened = new List(2); private static readonly FieldRef> Stack = AccessTools.FieldRefAccess>("popupStack"); private static readonly FieldInfo Instance = AccessTools.Field(typeof(UnifiedPopup), "instance"); private static bool _passing; private static int _closedFrame = -1; public static bool HasProvider => Providers.Current != null; public static bool BridgeEnabled { get { if (Plugin.BridgeDialogs != null) { return Plugin.BridgeDialogs.Value; } return false; } } public static bool IsOpen => Opened.Count > 0; public static Dialog Top { get { if (Opened.Count <= 0) { return null; } return Opened[Opened.Count - 1]; } } public static bool InputOpen => Opened.Exists(IsInput); public static bool ClosedThisFrame => _closedFrame == Time.frameCount; public static void Register(string owner, DialogProvider provider, int priority = 0) { List orphans = Release(owner); Providers.Set(owner, provider, priority); Replace(orphans); } public static bool Unregister(string owner) { List orphans = Release(owner); bool result = Providers.Remove(owner); Replace(orphans); return result; } public static ConfirmDialog Confirm(string title, string text, Action yes, Action no = null, string yesText = null, string noText = null) { PopupButtonCallback val = default(PopupButtonCallback); PopupButtonCallback val4 = default(PopupButtonCallback); ConfirmDialog obj = new ConfirmDialog(yes, no) { Title = Localize(title), Text = Localize(text), YesText = yesText, NoText = noText, Fallback = delegate { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_002a: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_0049: Expected O, but got Unknown string text2 = title; string text3 = text; PopupButtonCallback obj2 = val; if (obj2 == null) { PopupButtonCallback val2 = delegate { UnifiedPopup.Pop(); yes?.Invoke(); }; PopupButtonCallback val3 = val2; val = val2; obj2 = val3; } PopupButtonCallback obj3 = val4; if (obj3 == null) { PopupButtonCallback val5 = delegate { UnifiedPopup.Pop(); no?.Invoke(); }; PopupButtonCallback val3 = val5; val4 = val5; obj3 = val3; } UnifiedPopup.Push((PopupBase)new YesNoPopup(text2, text3, obj2, obj3, true, true)); } }; Open(obj); return obj; } public static ChoiceDialog Choose(string title, string text, params DialogButton[] buttons) { if (buttons == null || buttons.Length == 0) { throw new ArgumentException("at least one button is required", "buttons"); } DialogButton[] array = buttons; for (int i = 0; i < array.Length; i++) { if (array[i] == null) { throw new ArgumentNullException("buttons"); } } PopupButtonCallback val = default(PopupButtonCallback); PopupButtonCallback val4 = default(PopupButtonCallback); PopupButtonCallback val6 = default(PopupButtonCallback); ChoiceDialog obj = new ChoiceDialog(buttons) { Title = Localize(title), Text = Localize(text), Fallback = delegate { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown //IL_006c: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0035: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_008b: Expected O, but got Unknown if (buttons.Length == 1) { string text2 = title; string text3 = text; PopupButtonCallback obj2 = val; if (obj2 == null) { PopupButtonCallback val2 = delegate { UnifiedPopup.Pop(); buttons[0].Click?.Invoke(); }; PopupButtonCallback val3 = val2; val = val2; obj2 = val3; } UnifiedPopup.Push((PopupBase)new WarningPopup(text2, text3, obj2, true)); } else { string text4 = title; string text5 = text; PopupButtonCallback obj3 = val4; if (obj3 == null) { PopupButtonCallback val5 = delegate { UnifiedPopup.Pop(); buttons[0].Click?.Invoke(); }; PopupButtonCallback val3 = val5; val4 = val5; obj3 = val3; } PopupButtonCallback obj4 = val6; if (obj4 == null) { PopupButtonCallback val7 = delegate { UnifiedPopup.Pop(); buttons[1].Click?.Invoke(); }; PopupButtonCallback val3 = val7; val6 = val7; obj4 = val3; } UnifiedPopup.Push((PopupBase)new YesNoPopup(text4, text5, obj3, obj4, true, true)); } } }; Open(obj); return obj; } public static MessageDialog Message(string title, string text, Action ok = null, string okText = null) { PopupButtonCallback val = default(PopupButtonCallback); MessageDialog obj = new MessageDialog(ok) { Title = Localize(title), Text = Localize(text), OkText = okText, Fallback = delegate { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_002a: Expected O, but got Unknown string text2 = title; string text3 = text; PopupButtonCallback obj2 = val; if (obj2 == null) { PopupButtonCallback val2 = delegate { UnifiedPopup.Pop(); ok?.Invoke(); }; PopupButtonCallback val3 = val2; val = val2; obj2 = val3; } UnifiedPopup.Push((PopupBase)new WarningPopup(text2, text3, obj2, true)); } }; Open(obj); return obj; } public static InputDialog Input(string title, string value, Action submit, Action cancel = null, int charLimit = 0, Func validate = null, string placeholder = null) { PopupButtonCallback val = default(PopupButtonCallback); ValidateTextEntry val4 = default(ValidateTextEntry); RetrieveTextEntryResult val7 = default(RetrieveTextEntryResult); InputDialog obj = new InputDialog(submit, cancel, validate) { Title = Localize(title), Value = value, CharLimit = charLimit, Placeholder = placeholder, Fallback = delegate { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_0030: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_004f: Expected O, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_006e: Expected O, but got Unknown string text = title; string text2 = value; string text3 = placeholder; PopupButtonCallback obj2 = val; if (obj2 == null) { PopupButtonCallback val2 = delegate { UnifiedPopup.Pop(); cancel?.Invoke(); }; PopupButtonCallback val3 = val2; val = val2; obj2 = val3; } ValidateTextEntry obj3 = val4; if (obj3 == null) { ValidateTextEntry val5 = (string s) => validate == null || validate(s); ValidateTextEntry val6 = val5; val4 = val5; obj3 = val6; } RetrieveTextEntryResult obj4 = val7; if (obj4 == null) { RetrieveTextEntryResult val8 = delegate(string s) { UnifiedPopup.Pop(); submit?.Invoke(s); }; RetrieveTextEntryResult val9 = val8; val7 = val8; obj4 = val9; } UnifiedPopup.Push((PopupBase)new TextEntryPopup(text, text2, text3, obj2, obj3, obj4, true)); } }; Open(obj); return obj; } public static void Close(Dialog dialog) { if (dialog != null && dialog.IsOpen) { dialog.IsOpen = false; Opened.Remove(dialog); _closedFrame = Time.frameCount; Hide(Providers.Current, dialog); } } private static void Hide(DialogProvider p, Dialog dialog) { if (p == null) { return; } try { p.Hide(dialog); } catch (Exception e) { Plugin.Fail("DialogProvider " + dialog.Owner, e); } } internal static void DropStale() { for (int num = Opened.Count - 1; num >= 0; num--) { if (Opened[num].Frame < Time.frameCount) { Dialog dialog = Opened[num]; Close(dialog); Plugin.Trace("dialog dropped on a scene change: " + dialog.Title); } } } internal static void Finish(Dialog d) { if (d.IsOpen && Opened.Count > 0 && Opened[Opened.Count - 1] == d) { Close(d); } } internal static bool InterceptPush(PopupBase popup) { if (_passing || Providers.Current == null || !Plugin.BridgeDialogs.Value) { return false; } Dialog dialog = FromPopup(popup); if (dialog == null) { return false; } dialog.Vanilla = true; dialog.Fallback = delegate { UnifiedPopup.Push(popup); }; return Present(dialog); } internal static bool InterceptText(TextReceiver sign, string topic, int charLimit) { if (_passing || sign == null || Providers.Current == null || !Plugin.BridgeDialogs.Value) { return false; } return Present(new InputDialog((Action)sign.SetText, (Action)null, (Func)null) { Title = Localize(topic), Value = sign.GetText(), CharLimit = charLimit, Vanilla = true, Fallback = delegate { if ((Object)(object)TextInput.instance != (Object)null) { TextInput.instance.RequestText(sign, topic, charLimit); } } }); } internal static bool InterceptPop() { int count = Opened.Count; if (count == 0) { return false; } Dialog dialog = Opened[count - 1]; if (dialog.Depth != Depth()) { return false; } Close(dialog); return true; } private static Dialog FromPopup(PopupBase popup) { YesNoPopup val = (YesNoPopup)(object)((popup is YesNoPopup) ? popup : null); if (val != null) { return new ConfirmDialog((val.yesCallback == null) ? null : new Action(val.yesCallback.Invoke), (val.noCallback == null) ? null : new Action(val.noCallback.Invoke)) { Title = ((FixedPopupBase)val).header, Text = ((FixedPopupBase)val).text }; } WarningPopup val2 = (WarningPopup)(object)((popup is WarningPopup) ? popup : null); if (val2 != null) { PopupButtonCallback okCallback = val2.okCallback; return new MessageDialog((okCallback == null) ? null : new Action(okCallback.Invoke)) { Title = ((FixedPopupBase)val2).header, Text = ((FixedPopupBase)val2).text }; } TextEntryPopup val3 = (TextEntryPopup)(object)((popup is TextEntryPopup) ? popup : null); if (val3 != null) { PopupButtonCallback cancelCallback = val3.cancelCallback; RetrieveTextEntryResult sendResultCallback = val3.sendResultCallback; ValidateTextEntry validationCallback = val3.validationCallback; return new InputDialog((sendResultCallback == null) ? null : new Action(sendResultCallback.Invoke), (cancelCallback == null) ? null : new Action(cancelCallback.Invoke), (validationCallback == null) ? null : new Func(validationCallback.Invoke)) { Title = ((FixedPopupBase)val3).header, Text = ((FixedPopupBase)val3).text, Placeholder = val3.placeholderString }; } return null; } private static bool Present(Dialog d) { DialogProvider current = Providers.Current; if (current == null) { return false; } d.IsOpen = true; d.Depth = Depth(); d.Owner = Providers.Owner; d.Frame = Time.frameCount; Opened.Add(d); bool flag; try { flag = current.Show(d); } catch (Exception e) { Plugin.Fail("DialogProvider " + Providers.Owner, e); flag = false; } if (!flag && d.IsOpen) { d.IsOpen = false; Opened.Remove(d); } Plugin.Trace("dialog " + d.Kind.ToString() + (d.Vanilla ? " (game)" : "") + (flag ? "" : " (declined)") + ": " + d.Title); return flag; } private static void Open(Dialog d) { if (!Present(d)) { Pass(d.Fallback); } } private static List Release(string owner) { List list = null; for (int num = Opened.Count - 1; num >= 0; num--) { Dialog dialog = Opened[num]; if (!(dialog.Owner != owner)) { dialog.IsOpen = false; Opened.RemoveAt(num); Hide(Providers.Get(owner), dialog); (list ?? (list = new List(2))).Insert(0, dialog); } } return list; } private static void Replace(List orphans) { if (orphans == null) { return; } foreach (Dialog orphan in orphans) { if (!Present(orphan)) { Pass(orphan.Fallback); } } } private static void Pass(Action push) { if (push == null) { return; } _passing = true; try { push(); } finally { _passing = false; } } private static bool IsInput(Dialog d) { return d.Kind == DialogKind.Input; } private static string Localize(string s) { Localization instance = Localization.instance; if (instance != null && !string.IsNullOrEmpty(s)) { return instance.Localize(s); } return s; } private static int Depth() { object? value = Instance.GetValue(null); UnifiedPopup val = (UnifiedPopup)((value is UnifiedPopup) ? value : null); if (!((Object)(object)val == (Object)null)) { return Stack.Invoke(val).Count; } return 0; } } public sealed class HudElement { public bool Busy { get; set; } public bool Placed { get; set; } public string Owner { get; } public string Id { get; } public string Key => Owner + "/" + Id; public string Title { get; set; } public RectTransform Rect { get; } public bool Alive => (Object)(object)Rect != (Object)null; internal HudElement(string owner, string id, string title, RectTransform rect) { Owner = owner; Id = id; Title = title; Rect = rect; } } public abstract class HudProvider { public abstract void Added(HudElement element); public virtual void Removed(HudElement element) { } } public static class HudElements { private static readonly Slot Providers = new Slot(); private static readonly List Items = new List(8); private static HudProvider _fed; public static bool HasProvider => Providers.Current != null; public static void Register(string owner, HudProvider provider, int priority = 0) { Providers.Set(owner, provider, priority); Feed(); } public static bool Unregister(string owner) { if (!Providers.Remove(owner)) { return false; } Feed(); return true; } public static HudElement Add(string owner, string id, string title, RectTransform rect) { if (string.IsNullOrEmpty(owner)) { throw new ArgumentException("owner is required", "owner"); } if (string.IsNullOrEmpty(id)) { throw new ArgumentException("id is required", "id"); } if ((Object)(object)rect == (Object)null) { throw new ArgumentNullException("rect"); } Prune(); for (int i = 0; i < Items.Count; i++) { if (Items[i].Owner == owner && Items[i].Id == id) { Drop(i); } } HudElement hudElement = new HudElement(owner, id, title, rect); Items.Add(hudElement); Notify(Providers.Current, hudElement); return hudElement; } public static bool Remove(HudElement element) { int num = Items.IndexOf(element); if (num < 0) { return false; } Drop(num); return true; } public static int RemoveAll(string owner) { int num = 0; for (int num2 = Items.Count - 1; num2 >= 0; num2--) { if (!(Items[num2].Owner != owner)) { Drop(num2); num++; } } return num; } public static HudElement Find(string owner, string id) { Prune(); for (int i = 0; i < Items.Count; i++) { if (Items[i].Owner == owner && Items[i].Id == id) { return Items[i]; } } return null; } public static HudElement[] Snapshot() { Prune(); return Items.ToArray(); } private static void Drop(int i) { HudElement element = Items[i]; Items.RemoveAt(i); HudProvider current = Providers.Current; if (current == null) { return; } try { current.Removed(element); } catch (Exception e) { Plugin.Fail("HudProvider " + Providers.Owner, e); } } private static void Prune() { for (int num = Items.Count - 1; num >= 0; num--) { if (!Items[num].Alive) { Drop(num); } } } private static void Feed() { HudProvider current = Providers.Current; if (current == _fed) { return; } _fed = current; if (current != null) { Prune(); for (int i = 0; i < Items.Count; i++) { Notify(current, Items[i]); } } } private static void Notify(HudProvider p, HudElement e) { if (p == null) { return; } try { p.Added(e); } catch (Exception e2) { Plugin.Fail("HudProvider " + Providers.Owner, e2); } } } public enum NoticeKind { TopLeft, Center, Unlock, Biome } public sealed class Notice { public bool Log { get; set; } = true; public NoticeKind Kind { get; set; } public string Raw { get; set; } public string Text { get; set; } public string Title { get; set; } public int Amount { get; set; } public Sprite Icon { get; set; } public bool Stinger { get; set; } public bool ShowWhenHidden { get; set; } public bool Vanilla { get; internal set; } } public abstract class NoticeProvider { public abstract bool Show(Notice notice); } [HarmonyPatch(typeof(MessageHud), "ShowMessage")] internal static class ShowMessagePatch { [HarmonyPrefix] private static bool Pre(MessageType type, string text, int amount, Sprite icon, bool showDespiteHiddenHUD, bool log) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 if ((int)type == 1) { return !Notices.Intercept(NoticeKind.TopLeft, text, null, amount, icon, showDespiteHiddenHUD, log, stinger: false); } if ((int)type == 2) { return !Notices.Intercept(NoticeKind.Center, text, null, amount, icon, showDespiteHiddenHUD, log, stinger: false); } return true; } } [HarmonyPatch(typeof(MessageHud), "QueueUnlockMsg")] internal static class QueueUnlockPatch { [HarmonyPrefix] private static bool Pre(Sprite icon, string topic, string description) { return !Notices.Intercept(NoticeKind.Unlock, description, topic, 0, icon, force: true, log: true, stinger: false); } } [HarmonyPatch(typeof(MessageHud), "ShowBiomeFoundMsg")] internal static class BiomeFoundPatch { [HarmonyPrefix] private static bool Pre(string text, bool playStinger) { return !Notices.Intercept(NoticeKind.Biome, text, null, 0, null, force: true, log: false, playStinger); } } public static class Notices { private static readonly Slot Providers = new Slot(); private static bool _passing; private static readonly Action Add = (Action)Delegate.CreateDelegate(typeof(Action), AccessTools.Method(typeof(MessageHud), "AddLog", (Type[])null, (Type[])null)); public static bool HasProvider => Providers.Current != null; public static bool BridgeEnabled { get { if (Plugin.BridgeNotices != null) { return Plugin.BridgeNotices.Value; } return false; } } public static event Action Shown; public static void Register(string owner, NoticeProvider provider, int priority = 0) { Providers.Set(owner, provider, priority); } public static bool Unregister(string owner) { return Providers.Remove(owner); } public static void Show(string text, NoticeKind kind = NoticeKind.TopLeft, int amount = 0, Sprite icon = null, bool log = true) { if (kind == NoticeKind.Unlock || kind == NoticeKind.Biome) { throw new ArgumentException("use Unlock or Biome", "kind"); } Send(Make(kind, text, null, amount, icon, log, vanilla: false)); } public static void Unlock(Sprite icon, string title, string text) { Notice notice = Make(NoticeKind.Unlock, text, title, 0, icon, log: true, vanilla: false); notice.ShowWhenHidden = true; Send(notice); } public static void Biome(string text, bool stinger = true) { Notice notice = Make(NoticeKind.Biome, text, null, 0, null, log: false, vanilla: false); notice.Stinger = stinger; notice.ShowWhenHidden = true; Send(notice); } public static void Send(Notice notice) { if (notice == null) { throw new ArgumentNullException("notice"); } if (notice.Raw == null) { notice.Raw = notice.Text; } if (notice.Text == null) { notice.Text = Localize(notice.Raw); } if (!Dispatch(notice)) { Pass(notice); } } internal static bool Intercept(NoticeKind kind, string raw, string title, int amount, Sprite icon, bool force, bool log, bool stinger) { if (_passing || (Providers.Current == null && Notices.Shown == null) || !Plugin.BridgeNotices.Value) { return false; } Notice notice = Make(kind, raw, title, amount, icon, log, vanilla: true); notice.ShowWhenHidden = force; notice.Stinger = stinger; return Dispatch(notice); } private static Notice Make(NoticeKind kind, string raw, string title, int amount, Sprite icon, bool log, bool vanilla) { return new Notice { Kind = kind, Raw = raw, Text = Localize(raw), Title = ((title == null) ? null : Localize(title)), Amount = amount, Icon = icon, Log = log, Vanilla = vanilla }; } private static string Localize(string s) { Localization instance = Localization.instance; if (instance != null && !string.IsNullOrEmpty(s)) { return instance.Localize(s); } return s; } private static bool Dispatch(Notice n) { if (Hud.IsUserHidden() && !n.ShowWhenHidden) { return true; } Plugin.Trace("notice " + n.Kind.ToString() + (n.Vanilla ? " (game)" : "") + ": " + n.Text); Action shown = Notices.Shown; if (shown != null) { Delegate[] invocationList = shown.GetInvocationList(); foreach (Delegate obj in invocationList) { try { ((Action)obj)(n); } catch (Exception e) { Plugin.Fail("Notices.Shown", e); } } } NoticeProvider current = Providers.Current; if (current == null) { return false; } bool flag; try { flag = current.Show(n); } catch (Exception e2) { Plugin.Fail("NoticeProvider " + Providers.Owner, e2); return false; } if (flag && n.Log) { Journal(n); } return flag; } private static void Journal(Notice n) { MessageHud instance = MessageHud.instance; if (!((Object)(object)instance == (Object)null) && n.Kind != NoticeKind.Biome) { Add(instance, (n.Kind == NoticeKind.Unlock) ? (n.Title + ": " + n.Text) : n.Text); } } private static void Pass(Notice n) { MessageHud instance = MessageHud.instance; if ((Object)(object)instance == (Object)null) { return; } _passing = true; try { switch (n.Kind) { case NoticeKind.TopLeft: instance.ShowMessage((MessageType)1, n.Raw, n.Amount, n.Icon, n.ShowWhenHidden, n.Log); break; case NoticeKind.Center: instance.ShowMessage((MessageType)2, n.Raw, n.Amount, n.Icon, n.ShowWhenHidden, n.Log); break; case NoticeKind.Unlock: instance.QueueUnlockMsg(n.Icon, n.Title, n.Raw); break; case NoticeKind.Biome: instance.ShowBiomeFoundMsg(n.Raw, n.Stinger); break; } } finally { _passing = false; } } } [BepInPlugin("seneaL.valheim.api", "SeneaL API", "1.0.1")] public class Plugin : BaseUnityPlugin { public const string Version = "1.0.1"; internal static ManualLogSource Log; internal static ConfigEntry BridgeNotices; internal static ConfigEntry BridgeDialogs; internal static ConfigEntry BridgeAzu; internal static ConfigEntry BridgeStubs; internal static ConfigEntry StubIgnore; internal static ConfigEntry Verbose; private float _azuAt; private float _pollAt; private Harmony _harmony; private void Awake() { //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; BridgeNotices = ((BaseUnityPlugin)this).Config.Bind("Bridge", "Notices", true, "Route the game's own messages (top-left, center, unlock and biome banners) to the notice provider, if one is registered."); BridgeDialogs = ((BaseUnityPlugin)this).Config.Bind("Bridge", "Dialogs", true, "Route the game's own confirm, warning and text input windows to the dialog provider, if one is registered."); BridgeAzu = ((BaseUnityPlugin)this).Config.Bind("Bridge", "AzuEPI", true, "Show the equipment slots that other mods add through Azumatt's AzuEPI in the UI mod as slots of this API, if AzuEPI is installed. AzuEPI keeps running them; the UI only shows and moves the items."); BridgeStubs = ((BaseUnityPlugin)this).Config.Bind("Bridge", "AzuEPIStubs", true, "Route the AzuEPI API stubs that other mods carry inside their own dll (their methods do nothing) to this API, so such a mod's AddSlot reaches the UI mod (through AzuEPI if it is installed, otherwise emulated). Needs the patcher file in BepInEx/patchers and a game restart."); StubIgnore = ((BaseUnityPlugin)this).Config.Bind("Bridge", "AzuEPIStubIgnore", "Jewelcrafting,BowsBeforeHoes,BetterArchery,AdventureBackpacks,EpicLoot", "Assembly names (comma separated) of mods whose AzuEPI stub is left alone, because the UI mod already supports them itself. Remove a name to route that mod too. Needs a game restart."); AzuBridge.NameOf = PluginName; SlotOrigins.Fallback = delegate(string slot) { Assembly assembly = StubBridge.OriginOf(slot); return (!(assembly != null)) ? null : PluginName(assembly); }; if (StubIgnore.Value == "Jewelcrafting,BowsBeforeHoes,BetterArchery,AdventureBackpacks,EpicLoot,Backpacks,RustyBags,Hunter_Legacy,Magic_Supremacy,MagicPlugin,Wizardry" || StubIgnore.Value == "Jewelcrafting,BowsBeforeHoes,BetterArchery,AdventureBackpacks,EpicLoot,Magic_Supremacy") { StubIgnore.Value = "Jewelcrafting,BowsBeforeHoes,BetterArchery,AdventureBackpacks,EpicLoot"; } Verbose = ((BaseUnityPlugin)this).Config.Bind("Debug", "Verbose", false, "Log every routed notice and dialog."); SceneManager.activeSceneChanged += OnScene; _harmony = new Harmony("seneaL.valheim.api"); _harmony.PatchAll(typeof(Plugin).Assembly); Log.LogInfo((object)("SeneaL API 1.0.1 (contract " + 2 + ") loaded: " + string.Join(", ", Api.Features))); } private void Update() { float unscaledTime = Time.unscaledTime; if (unscaledTime >= _azuAt) { _azuAt = unscaledTime + 1f; TickAzu(); } if (unscaledTime >= _pollAt) { _pollAt = unscaledTime + 0.25f; if (Object.op_Implicit((Object)(object)Player.m_localPlayer)) { CustomSlots.Poll(Player.m_localPlayer); } } } private static void TickAzu() { if (!Chainloader.PluginInfos.TryGetValue("Azumatt.AzuExtendedPlayerInventory", out var value) || !Object.op_Implicit((Object)(object)value.Instance)) { if (AzuBridge.Bound) { AzuBridge.Clear(); } } else if (!BridgeAzu.Value || CustomSlots.AzuManaged) { if (AzuBridge.Bound) { AzuBridge.Clear(); } } else if (AzuBridge.Bind(((object)value.Instance).GetType().Assembly.GetType("AzuEPI.API"))) { AzuBridge.Sync(); } } private void Start() { StubHost.Start(); } private void OnDestroy() { SceneManager.activeSceneChanged -= OnScene; Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private static void OnScene(Scene from, Scene to) { Dialogs.DropStale(); } internal static void Trace(string text) { if (Verbose != null && Verbose.Value) { Log.LogInfo((object)text); } } internal static void Warn(string text) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)text); } } internal static void Fail(string where, Exception e) { Log.LogWarning((object)(where + ": " + e.GetType().Name + ": " + e.Message)); } private static string PluginName(Assembly asm) { foreach (PluginInfo value in Chainloader.PluginInfos.Values) { if (Object.op_Implicit((Object)(object)value.Instance) && ((object)value.Instance).GetType().Assembly == asm) { return value.Metadata.Name; } } return asm.GetName().Name; } } public sealed class ModSetting { public string Owner { get; } public string Mod { get; } public ConfigEntryBase Entry { get; } public Func Label { get; set; } public Func Tip { get; set; } public Func Options { get; set; } public Func Visible { get; set; } public string Group { get; set; } public bool Hold { get; set; } public string Text { get { try { string text = Label?.Invoke(); return string.IsNullOrEmpty(text) ? Entry.Definition.Key : text; } catch (Exception e) { Plugin.Fail("ModSetting label " + Owner, e); return Entry.Definition.Key; } } } public bool Shown { get { try { return Visible == null || Visible(); } catch (Exception e) { Plugin.Fail("ModSetting visible " + Owner, e); return true; } } } internal ModSetting(string owner, string mod, ConfigEntryBase entry) { Owner = owner; Mod = mod; Entry = entry; } } public static class ModSettings { private static readonly List Items = new List(16); public static IReadOnlyList All => Items; public static event Action Changed; public static ModSetting Add(string owner, string mod, ConfigEntryBase entry, Func label = null, Func tip = null) { if (string.IsNullOrEmpty(owner)) { throw new ArgumentException("owner is required", "owner"); } if (entry == null) { throw new ArgumentNullException("entry"); } for (int num = Items.Count - 1; num >= 0; num--) { if (Items[num].Owner == owner && Items[num].Entry == entry) { Items.RemoveAt(num); } } ModSetting modSetting = new ModSetting(owner, string.IsNullOrEmpty(mod) ? owner : mod, entry) { Label = label, Tip = tip }; Items.Add(modSetting); Raise(); return modSetting; } public static int RemoveAll(string owner) { int num = Items.RemoveAll((ModSetting s) => s.Owner == owner); if (num > 0) { Raise(); } return num; } public static void Refresh() { Raise(); } private static void Raise() { Action changed = ModSettings.Changed; if (changed == null) { return; } Delegate[] invocationList = changed.GetInvocationList(); foreach (Delegate obj in invocationList) { try { ((Action)obj)(); } catch (Exception e) { Plugin.Fail("ModSettings.Changed", e); } } } } public static class CustomSlots { private sealed class Entry { public SlotDef Def; public int Serial; public ItemData Seen; } private static readonly Slot Providers = new Slot(); private static readonly List Items = new List(8); private static SlotProvider _fed; internal static Func LocalPlayer = () => Player.m_localPlayer; private static int _serial; public static bool HasProvider => Providers.Current != null; public static bool AzuManaged { get; set; } public static event Action Changed; public static void Register(string owner, SlotProvider provider, int priority = 0) { Providers.Set(owner, provider, priority); Feed(); } public static bool Unregister(string owner) { if (!Providers.Remove(owner)) { return false; } Feed(); return true; } public static SlotDef Add(string owner, SlotDef slot) { if (string.IsNullOrEmpty(owner)) { throw new ArgumentException("owner is required", "owner"); } if (slot == null) { throw new ArgumentNullException("slot"); } Remove(owner, slot.Id); if (slot.Mode == SlotMode.Linked && slot.Source == null) { throw new ArgumentException("a Linked slot needs a Source", "slot"); } slot.Owner = owner; Items.Add(new Entry { Def = slot, Serial = ++_serial }); Notify(Providers.Current, slot, added: true); return slot; } public static bool Remove(string owner, string id) { for (int i = 0; i < Items.Count; i++) { SlotDef def = Items[i].Def; if (!(def.Owner != owner) && !(def.Id != id)) { Items.RemoveAt(i); Notify(Providers.Current, def, added: false); return true; } } return false; } public static int RemoveAll(string owner) { int num = 0; for (int num2 = Items.Count - 1; num2 >= 0; num2--) { if (!(Items[num2].Def.Owner != owner)) { SlotDef def = Items[num2].Def; Items.RemoveAt(num2); Notify(Providers.Current, def, added: false); num++; } } return num; } public static string OriginOf(string slotName) { return SlotOrigins.Get(slotName); } public static SlotDef Find(string owner, string id) { for (int i = 0; i < Items.Count; i++) { SlotDef def = Items[i].Def; if (def.Owner == owner && def.Id == id) { return def; } } return null; } public static SlotDef[] Snapshot() { List list = new List(Items); list.Sort(Compare); SlotDef[] array = new SlotDef[list.Count]; for (int i = 0; i < array.Length; i++) { array[i] = list[i].Def; } return array; } public static ItemData Get(string owner, string id) { SlotDef slotDef = Find(owner, id); SlotProvider current = Providers.Current; if (slotDef == null) { return null; } if (slotDef.Mode == SlotMode.Linked) { return Read(slotDef, LocalPlayer()); } if (current == null) { return null; } try { return current.Get(slotDef); } catch (Exception e) { Plugin.Fail("SlotProvider " + Providers.Owner, e); return null; } } public static bool Set(string owner, string id, ItemData item) { SlotDef slotDef = Find(owner, id); SlotProvider current = Providers.Current; if (slotDef == null) { return false; } if (slotDef.Mode == SlotMode.Linked) { return Wear(slotDef, item); } if (current == null) { return false; } try { return current.Set(slotDef, item); } catch (Exception e) { Plugin.Fail("SlotProvider " + Providers.Owner, e); return false; } } internal static ItemData Read(SlotDef d, Player player) { try { return d.Source(player); } catch (Exception e) { Plugin.Fail("slot source " + d.Key, e); return null; } } private static bool Wear(SlotDef d, ItemData item) { Player val = LocalPlayer(); if (!Object.op_Implicit((Object)(object)val)) { return false; } ItemData val2 = Read(d, val); if (item == null) { if (val2 == null) { return true; } if (d.Take != null) { return Hand(d, d.Take, val, val2, "take"); } ((Humanoid)val).UnequipItem(val2, true); return true; } if (item == val2) { return true; } if (!d.Allows(item)) { return false; } if (d.Put != null) { return Hand(d, d.Put, val, item, "put"); } if (val2 != null) { ((Humanoid)val).UnequipItem(val2, true); } return ((Humanoid)val).EquipItem(item, true); } private static bool Hand(SlotDef d, Func f, Player player, ItemData item, string what) { try { return f(player, item); } catch (Exception e) { Plugin.Fail("slot " + what + " " + d.Key, e); return false; } } internal static void Poll(Player player) { for (int i = 0; i < Items.Count; i++) { Entry entry = Items[i]; if (entry.Def.Mode == SlotMode.Linked) { ItemData val = Read(entry.Def, player); if (val != entry.Seen) { ItemData seen = entry.Seen; entry.Seen = val; Report(new SlotChange(entry.Def, seen, val)); } } } } public static void Report(SlotChange change) { if (change == null) { throw new ArgumentNullException("change"); } Call(change.Slot.Changed, change); Action changed = CustomSlots.Changed; if (changed != null) { Delegate[] invocationList = changed.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { Call((Action)invocationList[i], change); } } } private static void Call(Action a, SlotChange c) { if (a == null) { return; } try { a(c); } catch (Exception e) { Plugin.Fail("slot callback " + c.Slot.Key, e); } } private static int Compare(Entry a, Entry b) { int num = a.Def.Order.CompareTo(b.Def.Order); if (num == 0) { return a.Serial.CompareTo(b.Serial); } return num; } private static void Feed() { SlotProvider current = Providers.Current; if (current == _fed) { return; } _fed = current; if (current != null) { SlotDef[] array = Snapshot(); foreach (SlotDef d in array) { Notify(current, d, added: true); } } } private static void Notify(SlotProvider p, SlotDef d, bool added) { if (p == null) { return; } try { if (added) { p.Added(d); } else { p.Removed(d); } } catch (Exception e) { Plugin.Fail("SlotProvider " + Providers.Owner, e); } } } public enum SlotMode { Stored, Worn, Linked } public enum SlotPlace { Auto, Row, Doll } public sealed class SlotDef { public bool EquipOnUse { get; set; } = true; public float EquipTime { get; set; } = -1f; public SlotPlace Place { get; set; } public string Id { get; } public string Owner { get; internal set; } public string Key => Owner + "/" + Id; public string Title { get; set; } public string Hint { get; set; } public string Origin { get; set; } public Sprite Icon { get; set; } public string IconName { get; set; } public SlotMode Mode { get; set; } public int Order { get; set; } public Func Source { get; set; } public Func Accepts { get; set; } public Func Visible { get; set; } public Func Put { get; set; } public Func Take { get; set; } public Action Changed { get; set; } public SlotDef(string id) { if (string.IsNullOrEmpty(id)) { throw new ArgumentException("id is required", "id"); } Id = id; } public bool Allows(ItemData item) { if (item != null) { if (Accepts != null) { return Accepts(item); } return true; } return false; } } public sealed class SlotChange { public SlotDef Slot { get; } public ItemData Old { get; } public ItemData New { get; } public SlotChange(SlotDef slot, ItemData old, ItemData next) { Slot = slot; Old = old; New = next; } } public abstract class SlotProvider { public abstract void Added(SlotDef slot); public virtual void Removed(SlotDef slot) { } public abstract ItemData Get(SlotDef slot); public abstract bool Set(SlotDef slot, ItemData item); } public enum ThemePart { Window, Inner, Card, Button, ButtonHover, Primary, PrimaryHover, Field, FieldFocus, Row, RowHover, RowOn, Tab, TabHover, TabOn, Track, Chip, Separator } public enum ThemeFont { Body, Strong, Title } public enum ThemeColor { Text, Muted, Accent, Good, Bad, Warn, OnPrimary, Shade } public abstract class ThemeProvider { public virtual float PixelsPerUnit => 0f; public virtual Sprite Sprite(ThemePart part) { return null; } public virtual TMP_FontAsset Font(ThemeFont font) { return null; } public virtual Sprite Icon(string name) { return null; } public virtual bool TryColor(ThemeColor color, out Color value) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) value = default(Color); return false; } } public static class Themes { private static readonly Slot Providers = new Slot(); public static bool HasProvider => Providers.Current != null; public static string Owner => Providers.Owner; public static float PixelsPerUnit { get { ThemeProvider current = Providers.Current; if (current == null) { return 0f; } try { return current.PixelsPerUnit; } catch (Exception e) { Plugin.Fail("ThemeProvider " + Providers.Owner, e); return 0f; } } } public static event Action Changed; public static void Register(string owner, ThemeProvider provider, int priority = 0) { ThemeProvider current = Providers.Current; Providers.Set(owner, provider, priority); if (current != Providers.Current || provider == Providers.Current) { Refresh(); } } public static bool Unregister(string owner) { ThemeProvider current = Providers.Current; if (!Providers.Remove(owner)) { return false; } if (current != Providers.Current) { Refresh(); } return true; } public static void Refresh() { Action changed = Themes.Changed; if (changed == null) { return; } Delegate[] invocationList = changed.GetInvocationList(); foreach (Delegate obj in invocationList) { try { ((Action)obj)(); } catch (Exception e) { Plugin.Fail("Themes.Changed", e); } } } public static Sprite Sprite(ThemePart part) { ThemeProvider current = Providers.Current; if (current == null) { return null; } try { return current.Sprite(part); } catch (Exception e) { Plugin.Fail("ThemeProvider " + Providers.Owner, e); return null; } } public static Sprite Icon(string name) { ThemeProvider current = Providers.Current; if (current == null || string.IsNullOrEmpty(name)) { return null; } try { return current.Icon(name); } catch (Exception e) { Plugin.Fail("ThemeProvider " + Providers.Owner, e); return null; } } public static TMP_FontAsset Font(ThemeFont font) { ThemeProvider current = Providers.Current; if (current == null) { return null; } try { return current.Font(font); } catch (Exception e) { Plugin.Fail("ThemeProvider " + Providers.Owner, e); return null; } } public static bool TryColor(ThemeColor color, out Color value) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) value = default(Color); ThemeProvider current = Providers.Current; if (current == null) { return false; } try { return current.TryColor(color, out value); } catch (Exception e) { Plugin.Fail("ThemeProvider " + Providers.Owner, e); return false; } } } }