Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Hoard v1.1.2
Hoard.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using ConditionalConfigSync; using HarmonyLib; using Hoard.Access; using Hoard.Admin; using Hoard.Compat; using Hoard.EpicLoot; using Hoard.Items; using Hoard.Net; using Hoard.Pieces; using Hoard.Pure; using Hoard.Store; using Hoard.UI; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("Hoard")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+0c3b3a277cdc085718f64cb0ea3abc5ec79b8cfc")] [assembly: AssemblyProduct("Hoard")] [assembly: AssemblyTitle("Hoard")] [assembly: InternalsVisibleTo("HoardHarnessDriver")] [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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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; } } internal static class IsExternalInit { } } namespace Hoard { internal static class Bind { public static MethodInfo Method(Type type, string name, Type[] parameters, Type returns, bool isStatic = false) { string text = type.Name + "." + name + "(" + string.Join(",", Array.ConvertAll(parameters, (Type p) => p.Name)) + ")"; MethodInfo methodInfo = AccessTools.DeclaredMethod(type, name, parameters, (Type[])null); if (methodInfo == null) { throw new MissingMethodException(text + " not found"); } if (methodInfo.IsStatic != isStatic) { throw new MissingMethodException(text + " is " + (methodInfo.IsStatic ? "static" : "an instance method") + ", expected " + (isStatic ? "static" : "instance")); } if (methodInfo.ReturnType != returns) { throw new MissingMethodException(text + " returns " + methodInfo.ReturnType.Name + ", expected " + returns.Name); } return methodInfo; } public static FieldInfo Field(Type type, string name, Type fieldType) { FieldInfo fieldInfo = AccessTools.Field(type, name); if (fieldInfo == null) { throw new MissingFieldException(type.Name + "." + name + " not found"); } if (fieldInfo.FieldType != fieldType) { throw new MissingFieldException(type.Name + "." + name + " is " + fieldInfo.FieldType.Name + ", expected " + fieldType.Name); } return fieldInfo; } public static FieldRef<TObj, TField> FieldRef<TObj, TField>(string name) { Field(typeof(TObj), name, typeof(TField)); return AccessTools.FieldRefAccess<TObj, TField>(name); } public static T Delegate<T>(MethodInfo mi) where T : Delegate { return (T)System.Delegate.CreateDelegate(typeof(T), mi); } } internal static class Cfg { internal static readonly ConfigSync Sync = new ConfigSync("helix.hoard") { DisplayName = "Hoard", CurrentVersion = "1.1.2", MinimumRequiredVersion = "1.1.2", ModRequired = true }; public const string SecGeneral = "1 General"; public const string SecHoard = "2 Hoard"; public const string SecTiers = "3 Tiers"; public const string SecRuneCache = "4 Rune Cache"; public const string SecStorage = "5 Storage"; public const string SecClient = "9 Client"; private static ConfigFile _cfg = null; private static int _order; private static DateTime _lastWrite; public static ConfigEntry<bool> LockConfiguration = null; public static ConfigEntry<float> ConfigReloadSeconds = null; public static ConfigEntry<string> HoardBuildCost = null; public static ConfigEntry<float> AccessRange = null; public static ConfigEntry<float> AccessTolerance = null; public static ConfigEntry<int> MaxItemsPerPut = null; public static ConfigEntry<int> BaseSlots = null; public static readonly ConfigEntry<string>[] TierRows = new ConfigEntry<string>[7]; public static ConfigEntry<string> RuneCacheBuildCost = null; public static ConfigEntry<int> RuneCacheFreeRows = null; public static ConfigEntry<float> RuneCacheTableRange = null; public static ConfigEntry<int> SnapshotCopies = null; public static ConfigEntry<int> JournalMaxMB = null; public static ConfigEntry<float> SnapshotDebounceSeconds = null; public static ConfigEntry<float> UiScale = null; public static ConfigEntry<bool> TooltipCompare = null; public static ConfigEntry<LogDetail> LogLevel = null; public static ConfigEntry<bool> ShelfView = null; private static readonly string?[] LastValidRows = new string[7]; public const string SecWards = "6 Wards"; public static ConfigEntry<bool> WardStores = null; public static ConfigEntry<OffWardAccess> OffWardAccess = null; public static ConfigEntry<float> WardRange = null; public static ConfigEntry<int> HistoryLength = null; public static ConfigEntry<UnwardedHoards> UnwardedHoards = null; public static TierTable Tiers { get; private set; } = TierTable.Parse(40, TierTable.Defaults); public static event Action? Changed; internal static event Action? UiSettingsChanged; public static event Action<string>? WardSettingChanged; public static void Init(ConfigFile cfg) { _cfg = cfg; LockConfiguration = cfg.Bind<bool>("1 General", "Lock Configuration", true, Desc("On: only server admins can change the synced settings (in game with a configuration manager, or in the server's cfg file). Other players can never change them, so leave this on. Always server-controlled.")); ((ConditionalConfigSync)Sync).AddLockingConfigEntry<bool>(LockConfiguration); ConfigReloadSeconds = Server("1 General", "ConfigReloadSeconds", 5f, "How often the server checks this cfg file for hand edits and reloads it (then pushes the values to players). 0 = off.", (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 300f)); HoardBuildCost = Server("2 Hoard", "BuildCost", "Wood:20, Stone:10, Resin:5", "Cost to build a Hoard piece (built with the hammer at a workbench). Prefab:amount, comma separated."); AccessRange = Server("2 Hoard", "AccessRange", 5f, "Players must stand within this many metres of a Hoard piece to open it, take, deposit or upgrade.", (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 20f)); AccessTolerance = Server("2 Hoard", "AccessTolerance", 2f, "Extra metres the server allows on top of AccessRange, because player positions reach it with a little lag.", (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 10f)); MaxItemsPerPut = Server("2 Hoard", "MaxItemsPerPut", 10, "Most items sent to the server in one deposit message (Deposit all sends several messages).", (AcceptableValueBase?)(object)new AcceptableValueRange<int>(1, 20)); BaseSlots = Server("3 Tiers", "Base Slots", 40, "Hoard capacity at tier 0, before any upgrade.", (AcceptableValueBase?)(object)new AcceptableValueRange<int>(1, 5000)); for (int i = 0; i < 7; i++) { int num = i + 1; TierRows[i] = Server("3 Tiers", $"Tier {num}", TierTable.Defaults[i], $"Upgrade to tier {num}. Format: gate; slots; materials. gate = world key of the boss that must be dead (- for none); slots = capacity at this tier (never lower than the tier before); materials = Prefab:n, Prefab:n (- for none). A row that doesn't parse is logged: while the server runs it keeps its last valid value; at startup it disables this tier and every tier above it, and a Hoard already at that tier falls back to the capacity of the highest valid row below until it's fixed (nothing stored is lost; deposits are refused while over capacity)."); } RuneCacheBuildCost = Server("4 Rune Cache", "BuildCost", "FineWood:10, Iron:8", "Cost to build a Rune Cache (built with the hammer, near an enchanting table). Prefab:amount, comma separated."); RuneCacheFreeRows = Server("4 Rune Cache", "FreeRows", 2, "Rows of loose cells below the shelf (8 each), for shard stones and etched runestones. The 30 shelf slots and the 18 loose cells beside them are always there. New Rune Caches take a change at once, existing ones when their area next loads; a Rune Cache never has fewer rows than its items need.", (AcceptableValueBase?)(object)new AcceptableValueRange<int>(0, 8)); RuneCacheTableRange = Server("4 Rune Cache", "TableRange", 10f, "A Rune Cache can only be placed while you stand within this many metres (measured horizontally) of an enchanting table.", (AcceptableValueBase?)(object)new AcceptableValueRange<float>(2f, 50f)); SnapshotCopies = Server("5 Storage", "SnapshotCopies", 10, "Timestamped copies of the Hoard file kept beside the world, one per world save (the newest are kept).", (AcceptableValueBase?)(object)new AcceptableValueRange<int>(1, 100)); JournalMaxMB = Server("5 Storage", "JournalMaxMB", 16, "Size in MB after which the Hoard journal is rotated. Segments still needed for recovery are always kept.", (AcceptableValueBase?)(object)new AcceptableValueRange<int>(1, 1024)); SnapshotDebounceSeconds = Server("5 Storage", "SnapshotDebounceSeconds", 2f, "Seconds after the last change before the Hoard file is rewritten. Every change is already safe in the journal.", (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0.5f, 30f)); UiScale = Local("9 Client", "UiScale", 1f, "Size of the Hoard window.", (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0.5f, 2f)); TooltipCompare = Local("9 Client", "TooltipCompare", def: true, "Show the item you have equipped in the same slot next to a hovered Hoard item."); LogLevel = Local("9 Client", "LogLevel", LogDetail.Info, "Quiet: warnings and errors only. Info: transactions, snapshots and binding status. Debug: also per-message detail."); ShelfView = Local("9 Client", "RuneCacheShelfView", def: true, "Show the Rune Cache as a labelled shelf (column and rarity labels, faded icons in empty slots). Off: the plain chest grid, with the same slots and rules."); BindStore(); BindPieces(); BindUi(); BaseSlots.SettingChanged += delegate { ReparseTiers(log: true); }; ConfigEntry<string>[] tierRows = TierRows; for (int num2 = 0; num2 < tierRows.Length; num2++) { tierRows[num2].SettingChanged += delegate { ReparseTiers(log: true); }; } ReparseTiers(log: true); cfg.SettingChanged += delegate { RaiseChanged(); }; RememberWriteTime(); } private static void BindStore() { WardStores = Server("6 Wards", "WardStores", def: true, "On: a Hoard inside a player's ward opens that ward's own store, which only the ward's owner and the players on its list can use. Off: every Hoard opens what a Hoard outside wards opens (see UnwardedHoards), and a ward only decides who may use a Hoard, as in 1.0. Ward stores keep their items while this is off."); OffWardAccess = Server("6 Wards", "OffWardAccess", Hoard.Pure.OffWardAccess.Members, "Who can open a ward's store while that ward is switched off. Members: its owner and everyone on its list (while a ward is off, anyone can add themselves to its list at the ward stone). OwnerOnly: its owner only. The Hoard window warns when its ward is off."); WardRange = Server("6 Wards", "WardRange", 0f, "Ward radius in metres used to decide which Hoards a ward covers. 0 = each ward's own radius (32 in the base game, or whatever a mod set on the ward). Set it when a mod changes the ward radius only inside the game, which the server can't see, to the radius that mod uses.", (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 200f)); HistoryLength = Server("6 Wards", "HistoryLength", 100, $"How many recent entries the History tab of a Hoard shows. 0 hides the tab. The server always keeps the last {100} per store.", (AcceptableValueBase?)(object)new AcceptableValueRange<int>(0, 100)); UnwardedHoards = Server("6 Wards", "UnwardedHoards", Hoard.Pure.UnwardedHoards.Shared, "What a Hoard outside every ward opens. Shared: the one shared store that every such Hoard opens, as in 1.0. Personal: each player's own store."); Watch<bool>(WardStores); Watch<OffWardAccess>(OffWardAccess); Watch<float>(WardRange); Watch<int>(HistoryLength); Watch<UnwardedHoards>(UnwardedHoards); } private static void BindPieces() { CheckCost(HoardBuildCost); CheckCost(RuneCacheBuildCost); HoardBuildCost.SettingChanged += delegate { PieceCosts.ApplyCost(vault: true, "setting changed"); }; RuneCacheBuildCost.SettingChanged += delegate { PieceCosts.ApplyCost(vault: false, "setting changed"); }; RuneCacheFreeRows.SettingChanged += delegate { PieceCosts.ApplyRows("setting changed"); }; } private static void BindUi() { UiScale.SettingChanged += delegate { RaiseUiSettings(); }; TooltipCompare.SettingChanged += delegate { RaiseUiSettings(); }; } private static void ReparseTiers(bool log) { string[] array = new string[7]; List<string> list = new List<string>(); for (int i = 0; i < array.Length; i++) { string text = TierRows[i]?.Value ?? TierTable.Defaults[i]; TierLine tierLine = TierTable.ParseLine(i + 1, text); if (tierLine.Valid) { LastValidRows[i] = text; } else if (LastValidRows[i] != null) { list.Add($"Tier {i + 1}: '{text}' doesn't parse ({tierLine.Error}); keeping the previous value '{LastValidRows[i]}' until it's fixed"); text = LastValidRows[i]; } array[i] = text; } TierTable tierTable = (Tiers = TierTable.Parse(BaseSlots?.Value ?? 40, array)); if (!log) { return; } foreach (string item in list) { Log.Warn("cfg", "tier table: " + item); } foreach (string warning in tierTable.Warnings) { Log.Warn("cfg", "tier table: " + warning); } Log.Info("cfg", $"tier table: base {tierTable.BaseSlots} slots, highest reachable tier {tierTable.HighestReachable} ({tierTable.SlotsFor(tierTable.HighestReachable)} slots)"); } private static void RaiseChanged() { try { Cfg.Changed?.Invoke(); } catch (Exception ex) { Log.Warn("cfg", "a settings listener threw " + Log.Innermost(ex)); } } internal static ConfigEntry<T> Server<T>(string section, string key, T def, string desc, AcceptableValueBase? range = null) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown return ((ConditionalConfigSync)Sync).AddConfigEntry<T>(_cfg, section, key, def, new ConfigDescription(desc + " Synced from the server.", range, new object[1] { new ConfigurationManagerAttributes { Order = (_order -= 1) } }), (ConfigSyncMode)0, true).SourceConfig; } internal static ConfigEntry<T> Local<T>(string section, string key, T def, string desc, AcceptableValueBase? range = null) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown return ((ConditionalConfigSync)Sync).AddConfigEntry<T>(_cfg, section, key, def, new ConfigDescription(desc + " Local to this machine.", range, new object[1] { new ConfigurationManagerAttributes { Order = (_order -= 1) } }), (ConfigSyncMode)2, false).SourceConfig; } private static ConfigDescription Desc(string text) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown return new ConfigDescription(text, (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = (_order -= 1) } }); } private static void RememberWriteTime() { try { _lastWrite = File.GetLastWriteTimeUtc(_cfg.ConfigFilePath); } catch { _lastWrite = DateTime.MinValue; } } public static void PollReload() { if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { return; } DateTime lastWriteTimeUtc; try { lastWriteTimeUtc = File.GetLastWriteTimeUtc(_cfg.ConfigFilePath); } catch { return; } if (!(lastWriteTimeUtc == _lastWrite)) { _lastWrite = lastWriteTimeUtc; try { _cfg.Reload(); } catch (Exception ex) { Log.Warn("cfg", "reload failed: " + ex.Message); return; } RememberWriteTime(); Log.Info("cfg", "config reloaded from disk: " + Summary()); } } public static string Summary() { return $"base {BaseSlots.Value} slots, highest tier {Tiers.HighestReachable} ({Tiers.SlotsFor(Tiers.HighestReachable)} slots), AccessRange {AccessRange.Value:0.#}+{AccessTolerance.Value:0.#} m, MaxItemsPerPut {MaxItemsPerPut.Value}, Rune Cache 8x{ShelfMap.Height(RuneCacheFreeRows.Value)} ({RuneCacheFreeRows.Value} free rows) within {RuneCacheTableRange.Value:0.#} m of a table, snapshots {SnapshotCopies.Value} copies"; } internal static List<Cost> PieceCost(ConfigEntry<string> entry) { if (TierTable.TryParseCosts(entry.Value, out List<Cost> costs, out string error)) { return costs; } Log.Warn("cfg", $"{((ConfigEntryBase)entry).Definition.Section} {((ConfigEntryBase)entry).Definition.Key} '{entry.Value}': {error}; using the default '{((ConfigEntryBase)entry).DefaultValue}'"); TierTable.TryParseCosts((string)((ConfigEntryBase)entry).DefaultValue, out costs, out string _); return costs; } private static void CheckCost(ConfigEntry<string> entry) { if (!TierTable.TryParseCosts(entry.Value, out List<Cost> _, out string error)) { Log.Warn("cfg", $"{((ConfigEntryBase)entry).Definition.Section} {((ConfigEntryBase)entry).Definition.Key} '{entry.Value}' doesn't parse ({error}); the default '{((ConfigEntryBase)entry).DefaultValue}' is used"); } } private static void RaiseUiSettings() { try { Cfg.UiSettingsChanged?.Invoke(); } catch (Exception ex) { Log.Warn("ui", "a window settings listener threw " + Log.Innermost(ex)); } } internal static WardSettings WardSettingsNow() { return new WardSettings { WardStores = (WardStores?.Value ?? true), OffWardAccess = (OffWardAccess?.Value ?? Hoard.Pure.OffWardAccess.Members), WardRange = (WardRange?.Value ?? 0f), HistoryLength = (HistoryLength?.Value ?? 100), UnwardedHoards = (UnwardedHoards?.Value ?? Hoard.Pure.UnwardedHoards.Shared) }; } private static void Watch<T>(ConfigEntry<T> entry) { string key = ((ConfigEntryBase)entry).Definition.Key; entry.SettingChanged += delegate { Log.Info("cfg", $"{key} -> {entry.Value}"); try { Cfg.WardSettingChanged?.Invoke(key); } catch (Exception ex) { Log.Warn("cfg", "a ward-settings listener threw " + Log.Innermost(ex)); } }; } } internal sealed class ConfigurationManagerAttributes { public int? Order; public bool? Browsable; public bool? ReadOnly; public bool? IsAdvanced; public string? DispName; public string? Category; } internal interface IFeature { string Name { get; } bool Core { get; } void Init(); void RegisterPrefabs(); string? Patch(Harmony harmony); } internal static class Health { public static readonly List<string> Failures = new List<string>(); public static bool CoreOk { get; private set; } = true; public static void Fail(IFeature feature, string reason) { string text = feature.Name + ": " + reason; Failures.Add(text); if (feature.Core) { CoreOk = false; Log.Error("health", text + ". Hoard is OFF: the Hoard can't be opened and its pieces can't be built. Stored items are kept on disk untouched."); } else { Log.Error("health", text + ". That feature is off; the rest of Hoard keeps working."); } } } internal static class Keys { public const string AdminCommand = "hoard"; public const int AdminJournalLines = 40; public const int AdminReplyLines = 60; public const int AdminReplyChars = 12000; public const float AdminRefusalLogSeconds = 60f; public const string VaultPrefab = "hoard_vault"; public const string RuneCachePrefab = "hoard_runecache"; public const string EnchantingTablePrefab = "piece_enchantingtable"; public const string ZdoTier = "hoard_tier"; public const string RpcC2S = "hoard_C2S"; public const string RpcS2C = "hoard_S2C"; public const string RpcTier = "hoard_Tier"; public const string RpcAdmin = "hoard_Admin"; public const string RpcAdminResp = "hoard_AdminResp"; public const string MarkerPrefix = "helix.hoard."; public const string SacrificeFilterId = "helix.hoard.runecache"; public const string StoreExt = ".hoard"; public const string JournalExt = ".hoard.journal"; public const string ExportExt = ".hoardx"; public const string ClientDir = "Hoard"; public const string ClientJournalExt = ".hjl"; public const string UiPrefsExt = ".hui"; public const int CompressAbove = 1024; public const int MaxInflateBytes = 8388608; public const byte NetFlagCompressed = 1; public const float IdentityWaitSeconds = 15f; public const float PeerPollSeconds = 1f; public const int MaxDetailIds = 64; public const float ClientRetrySeconds = 5f; public const int MaxCachedDetails = 2048; public const string VaultBasePiece = "piece_chest_wood"; public const string RuneCacheBasePiece = "piece_chest_warderobe"; public static readonly string[] Tier0MeshPaths = new string[2] { "New/woodchest", "New/woodchesttop_closed" }; public const string TierChildPrefix = "Tier"; public const string VaultColliderChild = "HoardCollider"; public const float TierModelScale = 1.5f; public static readonly Vector3 VaultColliderSize = new Vector3(3.47f, 1.78f, 2.27f); public static readonly Vector3 VaultColliderCenter = new Vector3(0f, 0.89f, -0.03f); public const int RuneCacheColumns = 8; public const string WardPermittedIdPrefix = "pu_id"; public const string ZdoShelfMarker = "hoard_shelf"; public const string QuickStackStoreGuid = "goldenrevolver.quick_stack_store"; public const string MultiUserChestGuid = "com.maxsch.valheim.MultiUserChest"; public const string RecycleNReclaimGuid = "Azumatt.Recycle_N_Reclaim"; public const string UiRootName = "hoard_window"; public const string UiPrefsPrefix = "ui_"; public const string QssGuid = "goldenrevolver.quick_stack_store"; public const string QssUserConfigType = "QuickStackStore.UserConfig"; public const string AzuEpiGuid = "Azumatt.AzuExtendedPlayerInventory"; public const string AzuEpiApiType = "AzuEPI.API"; public const string EaqsGuid = "randyknapp.mods.equipmentandquickslots"; public const float UiHoverDelay = 0.12f; public const int UiPadPage = 10; public const float UpgradeRetrySeconds = 5f; public const int UpgradeMaxAttempts = 6; public const int TierMirrorCallsPerFrame = 16; private static TierModel[]? _tierModels; public const string CoinsChild = "Coins"; public const string CoinStackPrefab = "treasure_stack"; public const string CoinStackMesh = "coin_stack"; public const string CoinPilePrefab = "treasure_pile"; public const string CoinPileMesh = "treasure_pile"; public const string CoinStackChild = "Stack"; public const string CoinPileChild = "Pile"; public const float CoinPileScale = 1.3f; public static readonly Vector4[] CoinStackSpots = (Vector4[])(object)new Vector4[3] { new Vector4(1f, 0f, 0.3f, 0.15f), new Vector4(-1f, 0f, -0.3f, -0.1f), new Vector4(0.55f, 1f, 0f, 0.3f) }; public static readonly float[] CoinStackYaw = new float[3] { 0f, 40f, -70f }; public const int HistoryMax = 100; public const float MaxWardRadius = 200f; public const int MaxHeldPerPlayer = 32; public const double MaxHoldSeconds = 10.0; public const string RpcStores = "hoard_Stores"; public const string ZdoHasFields = "HasFields"; public const string ZdoHasFieldsPrivateArea = "HasFieldsPrivateArea"; public const string ZdoWardRadius = "PrivateArea.m_radius"; public const string GuardStonePrefab = "guard_stone"; public const float LookResolveSeconds = 1f; public const int LookResolvesPerFrame = 8; public const float DepositHoldSeconds = 2f; public const float ClaimRetrySeconds = 5f; public const int ClaimMaxAttempts = 6; public static TierModel[] TierModels { get { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) object obj = _tierModels; if (obj == null) { obj = new TierModel[5] { new TierModel("Tier0", "piece_chest_wood", Tier0MeshPaths, new Vector2(1.23f, 0.57f)), new TierModel("Tier2", "piece_chest", new string[2] { "New/ironchest", "New/ironchesttop_closed" }, new Vector2(1.38f, 0.73f)), new TierModel("Tier4", "piece_chest_blackmetal", new string[1] { "New" }, new Vector2(1.74f, 1.14f)), new TierModel("Tier6", "TreasureChest_dvergrtower", new string[2] { "New/woodchest", "New/woodchesttop_closed" }, new Vector2(1.05f, 0.58f)), new TierModel("Tier7", "piece_chest_grausten", new string[1] { "New" }, new Vector2(1.29f, 0.91f)) }; _tierModels = (TierModel[]?)obj; } return (TierModel[])obj; } } } internal sealed class TierModel { public readonly string Child; public readonly string Prefab; public readonly string[] Paths; public readonly Vector2 Half; public TierModel(string child, string prefab, string[] paths, Vector2 half) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) Child = child; Prefab = prefab; Paths = paths; Half = half; } } internal static class Loc { public static void Register() { Dictionary<string, string> dictionary = new Dictionary<string, string> { ["hoard_vault"] = "Hoard", ["hoard_vault_desc"] = "Opens the world's shared hoard of enchanted gear. Every Hoard piece opens the same contents; removing one loses nothing.", ["hoard_runecache"] = "Rune Cache", ["hoard_runecache_desc"] = "A chest for runestones, shard stones and enchanting materials. Build near an enchanting table.", ["hoard_open"] = "Open Hoard", ["hoard_refuse_storage"] = "The Hoard couldn't save that change (server storage error). Nothing moved.", ["hoard_refuse_unavailable"] = "The Hoard is unavailable right now (the log says why)." }; AddStore(dictionary); AddNet(dictionary); AddRows(dictionary); AddPieces(dictionary); AddUpgrade(dictionary); AddUi(dictionary); CustomLocalization localization = LocalizationManager.Instance.GetLocalization(); string text = "English"; localization.AddTranslation(ref text, dictionary); } private static void AddStore(Dictionary<string, string> d) { d["hoard_vault_desc"] = "Opens a hoard of enchanted gear. Inside your ward it opens that ward's own store, shared with the players on the ward's list; outside every ward it opens the server's shared store, or your own if the server is set that way. Removing a Hoard piece loses nothing."; d["hoard_refuse_wardstore"] = "Only $1's ward members can open this Hoard."; d["hoard_refuse_wardoff_owner"] = "This ward is off: only $1 can open this Hoard."; d["hoard_refuse_storechanged"] = "This Hoard now opens another store. Nothing moved; try again."; d["hoard_refuse_claim_notyours"] = "Only this ward's owner can claim a store here."; d["hoard_refuse_claim_hasstore"] = "This ward already has a store."; d["hoard_refuse_claim_notkept"] = "That store isn't waiting to be claimed any more."; d["hoard_refuse_claim_off"] = "Ward stores are turned off on this server."; d["hoard_refuse_history_off"] = "History is turned off on this server."; d["hoard_ui_title_shared"] = "Hoard · Shared"; d["hoard_ui_title_personal"] = "Hoard · Yours"; d["hoard_ui_title_ward"] = "Hoard · $1's ward"; d["hoard_ui_wardoff_members"] = "This ward is off: anyone can join it at its stone and open this Hoard."; d["hoard_ui_wardoff_owner"] = "This ward is off: only $1 can open this Hoard."; d["hoard_ui_footer_shared"] = "The shared store opens at any Hoard outside a ward."; d["hoard_ui_viewchanged"] = "This Hoard now opens another store."; d["hoard_ui_depositheld"] = "Checking which store this Hoard opens: your deposit goes in a moment."; d["hoard_ui_heldnotsent"] = "That deposit wasn't made: this Hoard's store changed or the window closed first. It stays with you."; d["hoard_ui_tab_items"] = "Items"; d["hoard_ui_tab_history"] = "History"; d["hoard_history_deposit"] = "deposited"; d["hoard_history_take"] = "took"; d["hoard_history_restore"] = "was given back (admin)"; d["hoard_history_upgrade"] = "upgraded it to tier $1"; d["hoard_history_created"] = "started this store"; d["hoard_history_claimed"] = "claimed this store here"; d["hoard_history_reattached"] = "moved this store here (admin)"; d["hoard_history_unknown"] = "Unknown item"; d["hoard_history_empty"] = "Nothing has happened here yet."; d["hoard_history_loading"] = "Loading the history..."; d["hoard_time_now"] = "just now"; d["hoard_time_min"] = "$1 min ago"; d["hoard_time_hours"] = "$1 h ago"; d["hoard_time_days"] = "$1 d ago"; d["hoard_claim_title"] = "Your kept stores"; d["hoard_claim_line"] = "$1 items · tier $2 · last used $3 · $4"; d["hoard_claim_orphan"] = "its ward is gone"; d["hoard_claim_off"] = "its ward is off"; d["hoard_claim_button"] = "Claim"; d["hoard_claim_note"] = "Depositing here starts a new store instead."; d["hoard_claim_done"] = "Store claimed: its items are here now."; d["hoard_claim_noanswer"] = "The Hoard didn't answer the claim. Nothing changed; try again."; d["hoard_hover_shared"] = "shared"; d["hoard_hover_personal"] = "yours"; d["hoard_hover_ward"] = "$1's ward"; d["hoard_hover_wardoff"] = "ward off"; } private static void AddNet(Dictionary<string, string> d) { d["hoard_refuse_taken"] = "Someone else took that item first."; d["hoard_refuse_unknownitem"] = "That item comes from a mod that isn't loaded. It stays safe in the Hoard."; d["hoard_refuse_full"] = "The Hoard is full. Upgrade it to store more."; d["hoard_refuse_notreceived"] = "The Hoard never received that item, so it stays with you."; d["hoard_refuse_bad"] = "The Hoard couldn't read that request."; d["hoard_refuse_toomany"] = "Too many items at once. Deposit the rest again."; d["hoard_refuse_range"] = "You're too far from the Hoard."; d["hoard_refuse_ward"] = "A ward protects this Hoard."; d["hoard_refuse_novault"] = "That Hoard is gone."; d["hoard_upgrade_unavailable"] = "Upgrading isn't available right now."; d["hoard_notready"] = "The Hoard is still connecting. Try again in a moment."; d["hoard_room"] = "Make room in your inventory first."; d["hoard_put_changed"] = "That item changed before it could be deposited. It stays in your inventory."; d["hoard_journal_error"] = "The Hoard couldn't write its journal on this computer. Nothing was moved."; d["hoard_take_timeout"] = "The Hoard didn't answer. Try again."; d["hoard_unbuildable"] = "An item the Hoard owes you comes from a mod that isn't loaded. It's kept for you until it is."; d["hoard_put_nosave"] = "This game doesn't save your character, so depositing is off."; d["hoard_put_savefailed"] = "Your character couldn't be saved, so that deposit didn't happen. Try it again."; } private static void AddRows(Dictionary<string, string> d) { foreach (KeyValuePair<string, string> item in new Dictionary<string, string> { ["hoard_stat_damagetotal"] = "Damage", ["hoard_stat_blunt"] = "Blunt", ["hoard_stat_slash"] = "Slash", ["hoard_stat_pierce"] = "Pierce", ["hoard_stat_fire"] = "Fire", ["hoard_stat_frost"] = "Frost", ["hoard_stat_lightning"] = "Lightning", ["hoard_stat_poison"] = "Poison", ["hoard_stat_spirit"] = "Spirit", ["hoard_stat_armor"] = "Armor", ["hoard_stat_blockpower"] = "Block", ["hoard_stat_blockforce"] = "Block force", ["hoard_stat_parry"] = "Parry ×", ["hoard_stat_maxdurability"] = "Max durability", ["hoard_stat_weight"] = "Weight", ["hoard_stat_movement"] = "Movement", ["hoard_group_weapons"] = "Weapons", ["hoard_group_shields"] = "Shields", ["hoard_group_armor"] = "Armor", ["hoard_group_other"] = "Other", ["hoard_unclassified"] = "Unclassified", ["hoard_unknown_item"] = "Unknown item", ["hoard_refuse_noepicloot"] = "The Hoard needs EpicLoot to recognise enchanted gear.", ["hoard_refuse_material"] = "Materials go in a Rune Cache.", ["hoard_refuse_unidentified"] = "Unidentified items can't be stored.", ["hoard_refuse_plain"] = "Plain gear isn't kept in the Hoard.", ["hoard_refuse_notequipment"] = "Only enchanted equipment is kept in the Hoard." }) { if (!d.ContainsKey(item.Key)) { d[item.Key] = item.Value; } } } private static void AddPieces(Dictionary<string, string> d) { d["hoard_runecache_needtable"] = "Build near an enchanting table."; d["hoard_refuse_runecache"] = "Only runestones, shard stones and enchanting materials go in a Rune Cache."; d["hoard_runecache_notempty"] = "Empty the Rune Cache before removing it."; d["hoard_open_failed"] = "The Hoard couldn't be opened. See the log for details."; d["hoard_runecache_desc"] = "A wardrobe with a shelf slot for every enchanting material (one stack each) and loose slots for shard stones and etched runestones. Build near an enchanting table."; d["hoard_shelf_full"] = "$1: its shelf slot is full."; d["hoard_shelf_blocked"] = "$1: its shelf slot holds $2. Move it out first."; d["hoard_shelf_otherstack"] = "$1: its shelf slot holds a stack that can't merge with this one."; d["hoard_shelf_stays"] = "$1 stays in its shelf slot."; d["hoard_shelf_looseonly"] = "Shelf slots are for their own material. Use a loose slot."; d["hoard_shelf_nofree"] = "No free loose cell in this Rune Cache."; d["hoard_shelf_ownslot"] = "$1 goes in its own shelf slot."; d["hoard_shelf_inuse"] = "Someone else is using this Rune Cache."; d["hoard_shelf_busy"] = "The Rune Cache changed while open, so it wasn't tidied. Close it and try again."; d["hoard_shelf_noreclaim"] = "Reclaim all doesn't work on a Rune Cache."; d["hoard_shelf_empty"] = "Empty shelf slot: holds one stack"; d["hoard_shelf_loose"] = "Loose"; d["hoard_shelf_divider"] = "Shard stones & etched runestones"; d["hoard_shelf_incomplete"] = "Some items in this Rune Cache didn't load (a missing mod?), so it isn't tidied."; } private static void AddUpgrade(Dictionary<string, string> d) { d["hoard_upgrade_max"] = "The Hoard is fully upgraded."; d["hoard_upgrade_invalid"] = "The next upgrade is misconfigured on the server (see its log)."; d["hoard_upgrade_gate"] = "Defeat this tier's boss first."; d["hoard_upgrade_materials"] = "You don't have all the materials."; d["hoard_upgrade_busy"] = "An upgrade is already in progress."; d["hoard_upgrade_stale"] = "The Hoard was already upgraded. Check the new cost."; d["hoard_upgrade_notopen"] = "Open a Hoard to upgrade it."; d["hoard_upgrade_noanswer"] = "The Hoard didn't answer the upgrade. Nothing was consumed."; d["hoard_upgrade_done"] = "The Hoard was upgraded."; d["hoard_upgrade_short"] = "The Hoard was upgraded, but only part of its cost was taken ($1, as far as this game can tell). The rest is free: the materials changed since you pressed Upgrade, or an effect such as Lucky Craft spared them."; } private static void AddUi(Dictionary<string, string> d) { Dictionary<string, string> dictionary = new Dictionary<string, string> { ["hoard_ui_tier"] = "Tier $1", ["hoard_ui_used"] = "$1 / $2 slots", ["hoard_ui_upgrade"] = "Upgrade", ["hoard_ui_depositall"] = "Deposit all magic gear", ["hoard_ui_next"] = "Next: tier $1 ($2 slots)", ["hoard_ui_needs"] = "needs $1 defeated", ["hoard_ui_maxtier"] = "The Hoard is fully upgraded.", ["hoard_ui_search"] = "Search names and enchantments", ["hoard_ui_anytype"] = "Any type", ["hoard_ui_anybiome"] = "Any biome", ["hoard_ui_more"] = "More", ["hoard_ui_clear"] = "Clear", ["hoard_ui_minupgrade"] = "Upgrade level at least", ["hoard_ui_setonly"] = "Set pieces only", ["hoard_ui_enchants"] = "Enchantments", ["hoard_ui_pick"] = "Pick", ["hoard_ui_picked"] = "$1 picked", ["hoard_ui_all"] = "ALL", ["hoard_ui_any"] = "ANY", ["hoard_ui_all_tip"] = "ALL: items with every picked enchantment. ANY: items with at least one.", ["hoard_ui_addstat"] = "+ Stat", ["hoard_ui_min"] = "min", ["hoard_ui_value"] = "value", ["hoard_ui_picker_search"] = "Search enchantments", ["hoard_ui_picker_none"] = "No enchantments in the Hoard yet.", ["hoard_ui_rarity_tip"] = "Show only this rarity (several can be on)", ["hoard_biome_none"] = "Starter", ["hoard_ui_col_name"] = "Name", ["hoard_ui_col_type"] = "Type · biome", ["hoard_ui_col_main"] = "Main stat", ["hoard_ui_col_ench"] = "Enchantments", ["hoard_ui_col_upg"] = "Level · durability", ["hoard_ui_unbreakable"] = "unbreakable", ["hoard_ui_taking"] = "taking...", ["hoard_ui_level"] = "Lv $1", ["hoard_ui_equipped"] = "Equipped", ["hoard_ui_held"] = "$1 item(s) waiting: make room", ["hoard_ui_depositing"] = "Depositing $1...", ["hoard_ui_loading"] = "Loading the Hoard...", ["hoard_ui_empty"] = "The Hoard is empty. Drag enchanted gear here, or use Deposit all.", ["hoard_ui_nomatch"] = "Nothing matches these filters.", ["hoard_ui_count"] = "$1 of $2", ["hoard_ui_hint"] = "Click or drag a row to take it. Drag gear here or Ctrl-click it to deposit.", ["hoard_ui_pad_take"] = "take", ["hoard_ui_pad_deposit"] = "deposit all", ["hoard_ui_pad_sort"] = "sort", ["hoard_ui_pad_switch"] = "list / inventory", ["hoard_ui_pad_close"] = "close", ["hoard_ui_pad_list"] = "Hoard list", ["hoard_ui_nobytes"] = "This item can't be saved, so it can't be deposited.", ["hoard_ui_notsent"] = "That item couldn't be deposited. It stays with you.", ["hoard_ui_nothing"] = "No enchanted gear to deposit. The hotbar, equipped items, equipment slots and favourites are skipped.", ["hoard_ui_didntfit"] = "$1 item(s) didn't fit. Upgrade the Hoard to store more.", ["hoard_ui_unavailable"] = "The Hoard window couldn't open on this computer (see the log)." }; foreach (KeyValuePair<string, string> item in new Dictionary<string, string> { ["swords"] = "Sword", ["axes"] = "Axe", ["twohandaxes"] = "Battleaxe", ["knives"] = "Knife", ["fists"] = "Fist weapon", ["staffs"] = "Staff", ["clubs"] = "Club", ["sledges"] = "Sledge", ["polearms"] = "Atgeir", ["spears"] = "Spear", ["bows"] = "Bow", ["bucklers"] = "Buckler", ["roundshields"] = "Round shield", ["towershields"] = "Tower shield", ["headarmor"] = "Helmet", ["chestarmor"] = "Chest armor", ["legsarmor"] = "Leg armor", ["shouldersarmor"] = "Cape", ["pickaxes"] = "Pickaxe", ["torches"] = "Torch", ["tools"] = "Tool", ["utility"] = "Utility", ["trinket"] = "Trinket" }) { dictionary["hoard_type_" + item.Key] = item.Value; } foreach (KeyValuePair<string, string> item2 in dictionary) { if (!d.ContainsKey(item2.Key)) { d[item2.Key] = item2.Value; } } } } internal static class Log { private static ManualLogSource? _src; public static bool DebugEnabled { get { if (Cfg.LogLevel != null) { return Cfg.LogLevel.Value == LogDetail.Debug; } return false; } } public static void Init(ManualLogSource src) { _src = src; } public static void Info(string area, string msg) { if (Cfg.LogLevel == null || Cfg.LogLevel.Value != LogDetail.Quiet) { ManualLogSource? src = _src; if (src != null) { src.LogInfo((object)("[" + area + "] " + msg)); } } } public static void Debug(string area, string msg) { if (DebugEnabled) { ManualLogSource? src = _src; if (src != null) { src.LogInfo((object)("[" + area + "] (debug) " + msg)); } } } public static void Warn(string area, string msg) { ManualLogSource? src = _src; if (src != null) { src.LogWarning((object)("[" + area + "] " + msg)); } } public static void Error(string area, string msg) { ManualLogSource? src = _src; if (src != null) { src.LogError((object)("[" + area + "] " + msg)); } } public static string Innermost(Exception ex) { while (ex.InnerException != null) { ex = ex.InnerException; } return ex.GetType().Name + ": " + ex.Message; } } internal enum LogDetail { Quiet, Info, Debug } [BepInPlugin("helix.hoard", "Hoard", "1.1.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency("_shudnal.ConditionalConfigSync", "1.0.9")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "helix.hoard"; public const string PluginName = "Hoard"; public const string PluginVersion = "1.1.2"; public const string CcsGuid = "_shudnal.ConditionalConfigSync"; public const string EpicLootGuid = "randyknapp.mods.epicloot"; internal static Plugin Instance = null; internal static Harmony Harmony = null; internal static readonly IFeature[] Features = new IFeature[9] { new BridgeFeature(), new StoreFeature(), new NetFeature(), new VaultFeature(), new RuneCacheFeature(), new TierFeature(), new UiFeature(), new ShelfViewFeature(), new AdminFeature() }; private bool _prefabsDone; internal static event Action? Tick; private void Awake() { Instance = this; Log.Init(((BaseUnityPlugin)this).Logger); Cfg.Init(((BaseUnityPlugin)this).Config); Loc.Register(); IFeature[] features = Features; foreach (IFeature feature in features) { Run(feature, "init", feature.Init); } PrefabManager.OnVanillaPrefabsAvailable += OnVanillaPrefabs; Log.Info("start", "Hoard 1.1.2: " + Cfg.Summary()); } private void Start() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown Harmony = new Harmony("helix.hoard"); IFeature[] features = Features; foreach (IFeature feature in features) { string text; try { text = feature.Patch(Harmony); } catch (Exception ex) { text = "patching threw " + Log.Innermost(ex); } if (text != null) { Health.Fail(feature, text); } } Log.Info("patch", (Health.Failures.Count == 0) ? $"all {Features.Length} features installed" : string.Format("{0} feature(s) off: {1}", Health.Failures.Count, string.Join("; ", Health.Failures.ToArray()))); Cfg.ConfigReloadSeconds.SettingChanged += delegate { ScheduleReload(); }; ScheduleReload(); } private void Update() { Action tick = Plugin.Tick; if (tick == null) { return; } Delegate[] invocationList = tick.GetInvocationList(); foreach (Delegate obj in invocationList) { try { ((Action)obj)(); } catch (Exception ex) { Tick -= (Action)obj; Log.Error("tick", obj.Method.DeclaringType?.Name + "." + obj.Method.Name + " threw " + Log.Innermost(ex) + "; that handler is removed"); } } } private void OnVanillaPrefabs() { if (!_prefabsDone) { _prefabsDone = true; PrefabManager.OnVanillaPrefabsAvailable -= OnVanillaPrefabs; IFeature[] features = Features; foreach (IFeature feature in features) { Run(feature, "prefabs", feature.RegisterPrefabs); } } } private static void Run(IFeature f, string stage, Action action) { try { action(); } catch (Exception ex) { Health.Fail(f, stage + " threw " + Log.Innermost(ex)); } } private void ScheduleReload() { ((MonoBehaviour)this).CancelInvoke("ReloadTick"); float value = Cfg.ConfigReloadSeconds.Value; if (value > 0f) { ((MonoBehaviour)this).InvokeRepeating("ReloadTick", Mathf.Max(1f, value), Mathf.Max(1f, value)); } } private void ReloadTick() { try { Cfg.PollReload(); } catch (Exception ex) { Log.Warn("cfg", "config poll failed: " + ex.Message); } } } } namespace Hoard.UI { internal sealed class ClaimBar { private sealed class Row { public Image Bg; public TMP_Text Text; public Button Claim; public uint StoreId; public bool Bound; } public const float RowH = 30f; public const float TitleH = 24f; public const int MaxRows = 3; private static readonly Color RowColor = new Color(0f, 0f, 0f, 0.22f); private static readonly Color SelectColor = new Color(0.65f, 0.45f, 0.18f, 0.6f); private readonly UiKit _kit; private readonly Action<uint> _claim; private readonly RectTransform _root; private readonly TMP_Text _title; private readonly TMP_Text _note; private readonly RectTransform _viewport; private readonly Scrollbar _bar; private readonly VList _list; private readonly List<Row> _rows = new List<Row>(); private IReadOnlyList<ClaimableStore> _stores = Array.Empty<ClaimableStore>(); private ClaimBarState _state; private int _selected = -1; private long _now; public int Count { get { if (_state != ClaimBarState.Hidden) { return _stores.Count; } return 0; } } public ClaimBar(UiKit kit, RectTransform parent, Action<uint> claim) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) _kit = kit; _claim = claim; _root = UiKit.NewRect((Transform)(object)parent, "claims"); Image obj = ((Component)_root).gameObject.AddComponent<Image>(); ((Graphic)obj).color = new Color(0.24f, 0.16f, 0.06f, 0.55f); ((Graphic)obj).raycastTarget = false; _title = kit.Text((Transform)(object)_root, "title", 15f, (TextAlignmentOptions)513, UiKit.Orange); _title.text = UiKit.L("$hoard_claim_title"); _note = kit.Text((Transform)(object)_root, "note", 14f, (TextAlignmentOptions)516, UiKit.Beige); _note.text = UiKit.L("$hoard_claim_note"); _viewport = UiKit.NewRect((Transform)(object)_root, "rows"); ((Component)_viewport).gameObject.AddComponent<RectMask2D>(); _bar = Popup.MakeScrollbar((Transform)(object)_root); _list = new VList(_viewport, _bar, 30f, Create, Bind); ((Component)_root).gameObject.SetActive(false); } public static float Height(int count) { if (count > 0) { return 24f + (float)Math.Min(count, 3) * 30f + 4f; } return 0f; } public void Place(float x, float y, float w, int count) { //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Expected O, but got Unknown float h = (float)Math.Max(1, Math.Min(count, 3)) * 30f; UiKit.Place(_root, x, y, w, Height(Math.Max(1, count))); UiKit.Place((Component)(object)_title, 8f, 2f, w * 0.35f, 22f); UiKit.Place((Component)(object)_note, w * 0.35f, 2f, w * 0.65f - 8f, 22f); bool flag = count > 3; float num = w - 4f - (flag ? 16f : 0f); UiKit.Place(_viewport, 2f, 24f, num, h); ((Component)_bar).gameObject.SetActive(flag); UiKit.Place((RectTransform)((Component)_bar).transform, num + 4f, 24f, 12f, h); _list.Refresh(); } public void Show(ClaimBarState state, IReadOnlyList<ClaimableStore> stores, int selected) { _state = state; IReadOnlyList<ClaimableStore> stores2; if (state != ClaimBarState.Hidden && stores != null) { stores2 = stores; } else { IReadOnlyList<ClaimableStore> readOnlyList = Array.Empty<ClaimableStore>(); stores2 = readOnlyList; } _stores = stores2; _selected = selected; _now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); bool flag = state != ClaimBarState.Hidden; if (((Component)_root).gameObject.activeSelf != flag) { ((Component)_root).gameObject.SetActive(flag); } if (flag) { _list.SetCount(_stores.Count); } } public uint StoreAt(int index) { if (index < 0 || index >= _stores.Count) { return 0u; } return _stores[index].StoreId; } public void ShowRow(int index) { if (index >= 0) { _list.Show(index); } } public void Tick() { if (_state != ClaimBarState.Hidden) { _list.Tick(1); } } private RectTransform Create(int slot) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) RectTransform val = UiKit.NewRect((Transform)(object)_viewport, "row" + slot); Row r = new Row { Bg = ((Component)val).gameObject.AddComponent<Image>() }; ((Graphic)r.Bg).color = RowColor; ((Graphic)r.Bg).raycastTarget = false; r.Text = _kit.Text((Transform)(object)val, "text", 15f, (TextAlignmentOptions)513, Color.white); r.Text.richText = false; UiKit.Stretch(r.Text.rectTransform, 8f, 0f, 112f, 0f); r.Claim = _kit.Button((Transform)(object)val, "claim", UiKit.L("$hoard_claim_button"), 15f, delegate { if (r.Bound && r.StoreId != 0 && _state == ClaimBarState.Ready) { _claim(r.StoreId); } }, vanilla: true); _rows.Add(r); return val; } private void Bind(int slot, int data) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) if (slot >= _rows.Count) { return; } Row row = _rows[slot]; row.Bound = data >= 0 && data < _stores.Count; if (row.Bound) { ClaimableStore claimableStore = _stores[data]; row.StoreId = claimableStore.StoreId; ((Graphic)row.Bg).color = ((data == _selected) ? SelectColor : RowColor); string text = UiLogic.ClaimLine(claimableStore, _now, UiKit.L); if (row.Text.text != text) { row.Text.text = text; } Button claim = row.Claim; Rect rect = _viewport.rect; UiKit.Place((Component)(object)claim, ((Rect)(ref rect)).width - 106f, 2f, 100f, 26f); ((Selectable)row.Claim).interactable = _state == ClaimBarState.Ready; } } } internal sealed class CompareTooltip { private sealed class Pane { public GameObject Root; public RectTransform Box; public TMP_Text Topic; public TMP_Text Text; } private readonly GameObject? _prefab; private readonly Transform _parent; private Pane? _a; private Pane? _b; private bool _reported; public bool Available => (Object)(object)_prefab != (Object)null; public bool Shown { get { if (_a != null) { return _a.Root.activeSelf; } return false; } } public CompareTooltip(GameObject? prefab, Transform canvasRoot) { _prefab = prefab; _parent = canvasRoot; } public void Hide() { if (_a != null) { _a.Root.SetActive(false); } if (_b != null) { _b.Root.SetActive(false); } } public void Show(ItemData item, ItemData? equipped, RectTransform window, float rowTop) { if (_a == null) { _a = Make("hoard_tooltip"); } if (_a == null) { return; } Fill(_a, ElUi.DecoratedName(item), item); if (equipped != null) { if (_b == null) { _b = Make("hoard_tooltip_equipped"); } if (_b != null) { Fill(_b, "<color=#aaaaaa><i>" + UiKit.L("$hoard_ui_equipped") + ":</i></color> " + ElUi.DecoratedName(equipped), equipped); } } else if (_b != null) { _b.Root.SetActive(false); } Arrange(window, rowTop, (equipped != null) ? _b : null); } private Pane? Make(string name) { //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Expected O, but got Unknown if ((Object)(object)_prefab == (Object)null) { return null; } try { GameObject val = Object.Instantiate<GameObject>(_prefab, _parent, false); ((Object)val).name = name; Transform obj = Utils.FindChild(val.transform, "Topic", (IterativeSearchType)0); TMP_Text val2 = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null); Transform obj2 = Utils.FindChild(val.transform, "Text", (IterativeSearchType)0); TMP_Text val3 = ((obj2 != null) ? ((Component)obj2).GetComponent<TMP_Text>() : null); if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null || val.transform.childCount == 0) { Object.Destroy((Object)(object)val); Report("the item tooltip prefab has no Topic/Text"); return null; } Graphic[] componentsInChildren = val.GetComponentsInChildren<Graphic>(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].raycastTarget = false; } return new Pane { Root = val, Box = (RectTransform)val.transform.GetChild(0), Topic = val2, Text = val3 }; } catch (Exception ex) { Report(Log.Innermost(ex)); return null; } } private void Report(string why) { if (!_reported) { _reported = true; Log.Warn("ui", "hover tooltips are off: " + why + " (reported once)"); } } private static void Fill(Pane p, string topic, ItemData item) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown string text; try { text = item.GetTooltip(-1); } catch (Exception ex) { text = Log.Innermost(ex); } p.Topic.text = topic; p.Text.text = Localization.instance.Localize(text); p.Root.transform.localScale = Vector3.one; p.Root.SetActive(true); p.Root.transform.SetAsLastSibling(); Canvas.ForceUpdateCanvases(); LayoutRebuilder.ForceRebuildLayoutImmediate(p.Box); LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)p.Root.transform); } private void Arrange(RectTransform window, float rowTop, Pane? b) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) Pane? a = _a; Vector3[] array = (Vector3[])(object)new Vector3[4]; window.GetWorldCorners(array); float num = 8f * ((Transform)window).lossyScale.x; FitHeight(a); if (b != null) { FitHeight(b); } Rect val = WorldRect(a.Box); Rect val2 = (Rect)((b != null) ? WorldRect(b.Box) : default(Rect)); float num2 = ((Rect)(ref val)).width + ((b != null) ? (num + ((Rect)(ref val2)).width) : 0f); float num3 = array[2].x + num; if (num3 + num2 > (float)Screen.width - 4f) { num3 = Mathf.Max(4f, (float)Screen.width - 4f - num2); } float top = Mathf.Min(rowTop, (float)Screen.height - 4f); MoveTo(a, num3, top); if (b != null) { MoveTo(b, num3 + ((Rect)(ref val)).width + num, top); } } private static void FitHeight(Pane p) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) Rect val = WorldRect(p.Box); float num = (float)Screen.height - 8f; if (((Rect)(ref val)).height > num && ((Rect)(ref val)).height > 0f) { float num2 = num / ((Rect)(ref val)).height; p.Root.transform.localScale = new Vector3(num2, num2, 1f); } } private static void MoveTo(Pane p, float left, float top) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) Rect val = WorldRect(p.Box); Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(left - ((Rect)(ref val)).xMin, top - ((Rect)(ref val)).yMax, 0f); Transform transform = p.Root.transform; transform.position += val2; Utils.ClampUIToScreen(p.Box); } private static Rect WorldRect(RectTransform rt) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) Vector3[] array = (Vector3[])(object)new Vector3[4]; rt.GetWorldCorners(array); return Rect.MinMaxRect(array[0].x, array[0].y, array[2].x, array[2].y); } } internal static class DepositAll { private delegate bool SlotAtFn(Inventory inv, Vector2i pos, out int slot); private static bool _bound; private static MethodInfo? _qssGetConfig; private static MethodInfo? _qssIsFavourite; private static SlotAtFn? _epiSlotAt; private static long _qssPlayer; private static object? _qssConfig; private static bool _reportedCall; internal static void EnsureBound() { if (!_bound) { _bound = true; BindQss(); BindAzuEpi(); if (Chainloader.PluginInfos.ContainsKey("randyknapp.mods.equipmentandquickslots")) { Log.Info("bind", "Equipment and Quick Slots is installed: Deposit all relies on its hotbar and equipped checks for its slots"); } } } private static Assembly? PluginAssembly(string guid) { if (!Chainloader.PluginInfos.TryGetValue(guid, out var value) || !((Object)(object)((value != null) ? value.Instance : null) != (Object)null)) { return null; } return ((object)value.Instance).GetType().Assembly; } private static void BindQss() { Assembly assembly = PluginAssembly("goldenrevolver.quick_stack_store"); if (assembly == null) { Log.Info("bind", "Quick Stack Store isn't installed: Deposit all has no favourites to skip"); return; } try { Type type = assembly.GetType("QuickStackStore.UserConfig", throwOnError: false) ?? throw new MissingMemberException("type QuickStackStore.UserConfig not found"); _qssGetConfig = Bind.Method(type, "GetPlayerConfig", new Type[1] { typeof(long) }, type, isStatic: true); _qssIsFavourite = Bind.Method(type, "IsItemNameOrSlotFavorited", new Type[1] { typeof(ItemData) }, typeof(bool)); Log.Info("bind", "ok Quick Stack Store favourites (UserConfig.GetPlayerConfig, IsItemNameOrSlotFavorited): Deposit all skips them"); } catch (Exception ex) { _qssGetConfig = (_qssIsFavourite = null); Log.Warn("bind", "failed Quick Stack Store favourites: " + Log.Innermost(ex) + ". Deposit all can't see favourites (it still skips the hotbar and equipped items)"); } } private static void BindAzuEpi() { Assembly assembly = PluginAssembly("Azumatt.AzuExtendedPlayerInventory"); if (assembly == null) { Log.Info("bind", "AzuExtendedPlayerInventory isn't installed: no equipment-slot cells to skip"); return; } try { MethodInfo method = Bind.Method(assembly.GetType("AzuEPI.API", throwOnError: false) ?? throw new MissingMemberException("type AzuEPI.API not found"), "TryGetSlotIndexAtGridPos", new Type[3] { typeof(Inventory), typeof(Vector2i), typeof(int).MakeByRefType() }, typeof(bool), isStatic: true); _epiSlotAt = (SlotAtFn)Delegate.CreateDelegate(typeof(SlotAtFn), method); Log.Info("bind", "ok AzuExtendedPlayerInventory API.TryGetSlotIndexAtGridPos: Deposit all skips its equipment and quick slots"); } catch (Exception ex) { _epiSlotAt = null; Log.Warn("bind", "failed AzuExtendedPlayerInventory slots: " + Log.Innermost(ex) + ". Deposit all relies on the equipped check for those cells"); } } internal static List<ItemData> Candidates(Player player) { EnsureBound(); List<ItemData> list = new List<ItemData>(); Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return list; } long playerID = player.GetPlayerID(); foreach (ItemData item in inventory.GetAllItemsInGridOrder()) { if (item != null) { bool equipped = item.m_equipped || ((Humanoid)player).IsItemEquiped(item) || GuiBinds.IsSheathed((Humanoid)(object)player, item); if (!UiLogic.DepositAllSkips(item.m_gridPos.y, equipped, EpiSlot(inventory, item), Favourite(playerID, item)) && Admission.ForHoard(ElBridge.Admission(item)).Accept) { list.Add(item); } } } return list; } private static bool EpiSlot(Inventory inv, ItemData item) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) if (_epiSlotAt == null) { return false; } try { int slot; return _epiSlotAt(inv, item.m_gridPos, out slot); } catch (Exception ex) { ReportCall("AzuExtendedPlayerInventory", ex); return true; } } private static bool Favourite(long playerId, ItemData item) { if (_qssGetConfig == null || _qssIsFavourite == null) { return false; } try { if (_qssConfig == null || _qssPlayer != playerId) { _qssConfig = _qssGetConfig.Invoke(null, new object[1] { playerId }); _qssPlayer = playerId; } return _qssConfig != null && (bool)_qssIsFavourite.Invoke(_qssConfig, new object[1] { item }); } catch (Exception ex) { ReportCall("Quick Stack Store", ex); return true; } } private static void ReportCall(string mod, Exception ex) { if (!_reportedCall) { _reportedCall = true; Log.Warn("ui", "Deposit all: a " + mod + " check threw " + Log.Innermost(ex) + "; such items are kept (reported once)"); } } } internal static class ElUi { private static bool _bound; private static Func<int, string>? _rarityColor; private static Func<int, string>? _rarityName; private static Func<ItemData, string?, string>? _decorated; private static readonly Dictionary<int, Color> Colors = new Dictionary<int, Color>(); public const int RarityCount = 6; internal static void EnsureBound() { if (_bound) { return; } _bound = true; Type api = ElBridge.ApiType; if (api == null) { Log.Warn("bind", "failed EpicLoot display helpers: EpicLoot.API isn't bound; rarity colours are white and names plain"); return; } List<string> list = new List<string>(); List<string> list2 = new List<string>(); Try(list, list2, "GetRarityColorByIndex", delegate { _rarityColor = Bind.Delegate<Func<int, string>>(Bind.Method(api, "GetRarityColorByIndex", new Type[1] { typeof(int) }, typeof(string), isStatic: true)); }); Try(list, list2, "GetRarityDisplayNameByIndex", delegate { _rarityName = Bind.Delegate<Func<int, string>>(Bind.Method(api, "GetRarityDisplayNameByIndex", new Type[1] { typeof(int) }, typeof(string), isStatic: true)); }); Try(list, list2, "GetItemDecoratedName", delegate { _decorated = Bind.Delegate<Func<ItemData, string, string>>(Bind.Method(api, "GetItemDecoratedName", new Type[2] { typeof(ItemData), typeof(string) }, typeof(string), isStatic: true)); }); if (list.Count > 0) { Log.Info("bind", "ok EpicLoot display helpers: " + string.Join(", ", list.ToArray())); } if (list2.Count > 0) { Log.Warn("bind", "failed EpicLoot display helpers: " + string.Join("; ", list2.ToArray()) + " (rarity colours fall back to white, names to the plain item name)"); } } private static void Try(List<string> ok, List<string> failed, string name, Action bind) { try { bind(); ok.Add(name); } catch (Exception ex) { failed.Add(name + ": " + Log.Innermost(ex)); } } internal static Color RarityColor(int rarity) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (rarity < 0 || _rarityColor == null) { return Color.white; } if (Colors.TryGetValue(rarity, out var value)) { return value; } value = Color.white; try { if (!ColorUtility.TryParseHtmlString(_rarityColor(rarity), ref value)) { value = Color.white; } } catch (Exception ex) { ElBridge.ReportCallFailure(ex); } Colors[rarity] = value; return value; } internal static string RarityName(int rarity) { if (_rarityName != null) { try { string text = _rarityName(rarity); if (!string.IsNullOrEmpty(text)) { return Localization.instance.Localize(text); } } catch (Exception ex) { ElBridge.ReportCallFailure(ex); } } return (rarity + 1).ToString(); } internal static string DecoratedName(ItemData item) { if (_decorated != null) { try { string text = _decorated(item, null); if (!string.IsNullOrEmpty(text)) { return Localization.instance.Localize(text); } } catch (Exception ex) { ElBridge.ReportCallFailure(ex); } } return Localization.instance.Localize(item.m_shared.m_name); } internal static void ResetColors() { Colors.Clear(); } } internal sealed class EnchantPicker { private const float W = 460f; private const float H = 392f; private const float LineH = 28f; private readonly UiKit _kit; private readonly GameObject _blocker; private readonly RectTransform _panel; private readonly TMP_InputField _search; private readonly Button _mode; private readonly TMP_Text _empty; private readonly RectTransform _viewport; private readonly VList _list; private readonly List<Button> _pickButtons = new List<Button>(); private readonly List<TMP_InputField> _mins = new List<TMP_InputField>(); private readonly List<string> _lineType = new List<string>(); private readonly List<(string Type, string Name, int Count)> _all = new List<(string, string, int)>(); private readonly List<(string Type, string Name, int Count)> _shown = new List<(string, string, int)>(); private QuerySpec _spec = new QuerySpec(); private Action _changed = delegate { }; public bool IsOpen => ((Component)_panel).gameObject.activeSelf; public EnchantPicker(UiKit kit, Transform canvasRoot) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Expected O, but got Unknown //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Expected O, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Expected O, but got Unknown //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_0372: Unknown result type (might be due to invalid IL or missing references) _kit = kit; Image val = UiKit.Image(canvasRoot, "hoard_picker_blocker", new Color(0f, 0f, 0f, 0f), raycast: true); UiKit.Stretch((RectTransform)((Component)val).transform, 0f, 0f, 0f, 0f); Button obj = ((Component)val).gameObject.AddComponent<Button>(); ((Selectable)obj).transition = (Transition)0; ((UnityEvent)obj.onClick).AddListener(new UnityAction(Close)); _blocker = ((Component)val).gameObject; Image val2 = UiKit.Image(canvasRoot, "hoard_picker", new Color(0.08f, 0.07f, 0.06f, 0.97f), raycast: true); _panel = (RectTransform)((Component)val2).transform; RectTransform panel = _panel; RectTransform panel2 = _panel; Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(0.5f, 0.5f); panel2.anchorMax = val3; panel.anchorMin = val3; _panel.pivot = new Vector2(0f, 1f); _panel.sizeDelta = new Vector2(460f, 392f); _search = kit.Input((Transform)(object)_panel, "search", UiKit.L("$hoard_ui_picker_search"), 15f, 40); UiKit.Place((Component)(object)_search, 8f, 8f, 250f, 28f); ((UnityEvent<string>)(object)_search.onValueChanged).AddListener((UnityAction<string>)delegate { UiKit.Guard("picker search", Filter); }); _mode = kit.Button((Transform)(object)_panel, "mode", "", 15f, ToggleMode); UiKit.Place((Component)(object)_mode, 266f, 8f, 80f, 28f); UiKit.Place((Component)(object)kit.Button((Transform)(object)_panel, "clear", UiKit.L("$hoard_ui_clear"), 15f, Clear), 352f, 8f, 100f, 28f); TMP_Text obj2 = kit.Text((Transform)(object)_panel, "hint", 13f, (TextAlignmentOptions)513, UiKit.Muted); UiKit.Place((Component)(object)obj2, 10f, 40f, 440f, 20f); obj2.text = UiKit.L("$hoard_ui_all_tip"); _viewport = UiKit.NewRect((Transform)(object)_panel, "lines"); UiKit.Place(_viewport, 8f, 64f, 430f, 320f); Scrollbar val4 = Popup.MakeScrollbar((Transform)(object)_panel); UiKit.Place((Component)(object)val4, 442f, 64f, 10f, 320f); _list = new VList(_viewport, val4, 28f, CreateLine, BindLine); _empty = kit.Text((Transform)(object)_panel, "empty", 15f, (TextAlignmentOptions)514, UiKit.Muted); UiKit.Place((Component)(object)_empty, 10f, 90f, 440f, 30f); _empty.text = UiKit.L("$hoard_ui_picker_none"); _blocker.SetActive(false); ((Component)_panel).gameObject.SetActive(false); } public IEnumerable<TMP_InputField> Inputs() { yield return _search; foreach (TMP_InputField min in _mins) { yield return min; } } public void Open(RectTransform anchor, QuerySpec spec, IReadOnlyList<Row> rows, Func<string, string> effectName, float scale, Action changed) { //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) _spec = spec; _changed = changed; _all.Clear(); foreach (KeyValuePair<string, int> item in Query.EffectCounts(rows)) { _all.Add((item.Key, effectName(item.Key), item.Value)); } foreach (EnchantPick p in spec.Enchants) { if (!_all.Exists(((string Type, string Name, int Count) e) => e.Type == p.Type)) { _all.Add((p.Type, effectName(p.Type), 0)); } } _blocker.transform.SetAsLastSibling(); ((Transform)_panel).SetAsLastSibling(); ((Transform)_panel).localScale = new Vector3(scale, scale, 1f); Vector3[] array = (Vector3[])(object)new Vector3[4]; anchor.GetWorldCorners(array); ((Transform)_panel).position = array[0]; _blocker.SetActive(true); ((Component)_panel).gameObject.SetActive(true); _search.SetTextWithoutNotify(""); Filter(); UpdateMode(); Utils.ClampUIToScreen(_panel); } public void Close() { foreach (TMP_InputField min in _mins) { if (min.isFocused) { min.DeactivateInputField(false); } } _blocker.SetActive(false); ((Component)_panel).gameObject.SetActive(false); } public void Tick() { if (IsOpen) { _list.Tick(2); if (ZInput.GetKeyDown((KeyCode)27, true)) { Close(); } } } private void Filter() { string text = _search.text?.Trim() ?? ""; _shown.Clear(); foreach (var item in _all) { if (text.Length == 0 || item.Name.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0) { _shown.Add(item); } } ((Component)_empty).gameObject.SetActive(_all.Count == 0); _list.SetCount(_shown.Count); _list.ScrollTo(0); } private void ToggleMode() { _spec.EnchantMode = ((_spec.EnchantMode == EnchantMode.All) ? EnchantMode.Any : EnchantMode.All); UpdateMode(); _changed(); } private void UpdateMode() { TMP_Text val = UiKit.Label(_mode); if ((Object)(object)val != (Object)null) { val.text = UiKit.L((_spec.EnchantMode == EnchantMode.All) ? "$hoard_ui_all" : "$hoard_ui_any"); } } private void Clear() { _spec.Enchants.Clear(); _list.Refresh(); _changed(); } private int PickIndex(string type) { return _spec.Enchants.FindIndex((EnchantPick p) => p.Type == type); } private RectTransform CreateLine(int slot) { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown RectTransform val = UiKit.NewRect((Transform)(object)_viewport, "line" + slot); Button val2 = _kit.Button((Transform)(object)val, "pick", "", 15f, delegate { TogglePick(slot); }); UiKit.Place((Component)(object)val2, 0f, 1f, 346f, 26f); TMP_Text val3 = UiKit.Label(val2); if ((Object)(object)val3 != (Object)null) { val3.alignment = (TextAlignmentOptions)513; UiKit.Stretch((RectTransform)val3.transform, 8f, 1f, 4f, 1f); } TMP_InputField val4 = _kit.Input((Transform)(object)val, "min", UiKit.L("$hoard_ui_min"), 14f, 8); UiKit.Place((Component)(object)val4, 352f, 1f, 76f, 26f); ((UnityEvent<string>)(object)val4.onEndEdit).AddListener((UnityAction<string>)delegate(string text) { UiKit.Guard("picker min", delegate { SetMin(slot, text); }); }); _pickButtons.Add(val2); _mins.Add(val4); _lineType.Add(""); return val; } private void BindLine(int slot, int data) { //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) if (slot < _pickButtons.Count && data >= 0) { (string Type, string Name, int Count) tuple = _shown[data]; string item = tuple.Type; string item2 = tuple.Name; int item3 = tuple.Count; TMP_InputField val = _mins[slot]; if (_lineType[slot] != item && val.isFocused) { val.DeactivateInputField(false); } _lineType[slot] = item; int num = PickIndex(item); bool flag = num >= 0; TMP_Text val2 = UiKit.Label(_pickButtons[slot]); if ((Object)(object)val2 != (Object)null) { val2.text = string.Format("{0}{1} <color=#a0a0a0>({2})</color>", flag ? "[x] " : "[ ] ", item2, item3); ((Graphic)val2).color = (flag ? UiKit.Orange : Color.white); } UiKit.Tint(_pickButtons[slot], flag ? new Color(0.42f, 0.3f, 0.14f, 0.95f) : new Color(0.12f, 0.1f, 0.08f, 0.9f)); float? num2 = (flag ? _spec.Enchants[num].Min : ((float?)null)); if (!val.isFocused) { val.SetTextWithoutNotify((!num2.HasValue) ? "" : num2.GetValueOrDefault().ToString("0.##", CultureInfo.InvariantCulture)); } } } private void TogglePick(int slot) { string text = _lineType[slot]; if (text.Length != 0) { int num = PickIndex(text); if (num >= 0) { _spec.Enchants.RemoveAt(num); } else { float value; float? min = (UiLogic.TryParseNumber(_mins[slot].text, out value) ? new float?(value) : ((float?)null)); _spec.Enchants.Add(new EnchantPick(text, min)); } _list.Refresh(); _changed(); } } private void SetMin(int slot, string text) { if (slot >= _lineType.Count) { return; } string text2 = _lineType[slot]; if (text2.Length == 0) { return; } float value; float? min = (UiLogic.TryParseNumber(text, out value) ? new float?(value) : ((float?)null)); int num = PickIndex(text2); if (num >= 0) { _spec.Enchants[num] = new EnchantPick(text2, min); } else { if (!min.HasValue) { return; } _spec.Enchants.Add(new EnchantPick(text2, min)); } _list.Refresh(); _changed(); } } internal interface IFilterHost { Availability Avail { get; } GameObject? TooltipPrefab { get; } void FiltersChanged(); void DrawerChanged(bool open); void OpenPopup(RectTransform anchor, List<PopupItem> items, float width); void OpenPicker(RectTransform anchor); } internal sealed class FilterBar { private sealed class Draft { public Stat Stat; public StatCmp Cmp; public string Text = ""; } private sealed class StatSlot { public RectTransform Root; public Button Stat; public Button Cmp; public TMP_InputField Value; public Button Remove; } public const float RowH = 30f; public const float DrawerH = 76f; private readonly UiKit _kit; private readonly IFilterHost _host; private readonly QuerySpec _spec; private readonly TMP_InputField _search; private readonly Button[] _chips = (Button[])(object)new Button[6]; private readonly Button _type; private readonly Button _biome; private readonly Button _more; private readonly Button _clear; private readonly RectTransform _drawer; private readonly TMP_Text _minLabel; private readonly Button _minDown; private readonly TMP_Text _minValue; private readonly Button _minUp; private readonly Button _set; private readonly TMP_Text _enchLabel; private readonly Button _pick; private readonly Button _mode; private readonly StatSlot[] _slots = new StatSlot[3]; private readonly Button _addStat; private readonly List<Draft> _drafts = new List<Draft>(); public bool DrawerOpen { get; private set; } public FilterBar(UiKit kit, IFilterHost host, QuerySpec spec, RectTransform parent, float x, float y, float width) { //IL_031b: Unknown result type (might be due to invalid IL or missing references) //IL_04c5: Unknown result type (might be due to invalid IL or missing references) _kit = kit; _host = host; _spec = spec; float num = x; _search = kit.Input((Transform)(object)parent, "search", UiKit.L("$hoard_ui_search"), 15f, 60); UiKit.Place((Component)(object)_search, num, y, 190f, 30f); ((UnityEvent<string>)(object)_search.onValueChanged).AddListener((UnityAction<string>)delegate(string t) { UiKit.Guard("search", delegate { _spec.Text = t ?? ""; _host.FiltersChanged(); }); }); num += 198f; for (int num2 = 0; num2 < _chips.Length; num2++) { int rarity = num2; Button val = kit.Button((Transform)(object)parent, "rarity" + num2, "", 14f, delegate { ToggleRarity(rarity); }); UiKit.Place((Component)(object)val, num + (float)num2 * 30f, y + 2f, 26f, 26f); _chips[num2] = val; GameObject tooltipPrefab = host.TooltipPrefab; if ((Object)(object)tooltipPrefab != (Object)null) { UITooltip obj = ((Component)val).gameObject.AddComponent<UITooltip>(); obj.m_tooltipPrefab = tooltipPrefab; obj.m_topic = ElUi.RarityName(rarity); obj.m_text = "$hoard_ui_rarity_tip"; } } num += (float)_chips.Length * 30f + 4f; _type = kit.Button((Transform)(object)parent, "type", "", 15f, OpenType); UiKit.Place((Component)(object)_type, num, y, 120f, 30f); num += 128f; _biome = kit.Button((Transform)(object)parent, "biome", "", 15f, OpenBiome); UiKit.Place((Component)(object)_biome, num, y, 120f, 30f); num += 128f; _more = kit.Button((Transform)(object)parent, "more", "", 15f, delegate { SetDrawer(!DrawerOpen); }); UiKit.Place((Component)(object)_more, num, y, 90f, 30f); num += 98f; _clear = kit.Button((Transform)(object)parent, "clear", UiKit.L("$hoard_ui_clear"), 15f, ClearAll); UiKit.Place((Component)(object)_clear, num, y, Mathf.Min(70f, x + width - num), 30f); _drawer = UiKit.NewRect((Transform)(object)parent, "drawer"); UiKit.Place(_drawer, x, y + 30f + 6f, width, 76f); float num3 = 0f; _minLabel = kit.Text((Transform)(object)_drawer, "minlabel", 15f, (TextAlignmentOptions)516, UiKit.Beige); UiKit.Place((Component)(object)_minLabel, num3, 0f, 168f, 30f); _minLabel.text = UiKit.L("$hoard_ui_minupgrade"); num3 += 174f; _minDown = kit.Button((Transform)(object)_drawer, "mindown", "-", 18f, delegate { StepMin(-1); }); UiKit.Place((Component)(object)_minDown, num3, 0f, 28f, 30f); _minValue = kit.Text((Transform)(object)_drawer, "minvalue", 16f, (TextAlignmentOptions)514); UiKit.Place((Component)(object)_minValue, num3 + 30f, 0f, 36f, 30f); _minUp = kit.Button((Transform)(object)_drawer, "minup", "+", 18f, delegate { StepMin(1); }); UiKit.Place((Component)(object)_minUp, num3 + 68f, 0f, 28f, 30f); num3 += 112f; _set = kit.Button((Transform)(object)_drawer, "set", UiKit.L("$hoard_ui_setonly"), 15f, ToggleSet); UiKit.Place((Component)(object)_set, num3, 0f, 150f, 30f); num3 += 166f; _enchLabel = kit.Text((Transform)(object)_drawer, "enchlabel", 15f, (TextAlignmentOptions)516, UiKit.Beige); UiKit.Place((Component)(object)_enchLabel, num3, 0f, 110f, 30f); _enchLabel.text = UiKit.L("$hoard_ui_enchants"); num3 += 116f; _pick = kit.Button((Transform)(object)_drawer, "pick", "", 15f, delegate { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown _host.OpenPicker((RectTransform)((Component)_pick).transform); }); UiKit.Place((Component)(object)_pick, num3, 0f, 124f, 30f); num3 += 130f; _mode = kit.Button((Transform)(object)_drawer, "mode", "", 15f, ToggleMode); UiKit.Place((Component)(object)_mode, num3, 0f, Mathf.Min(56f, width - num3), 30f); GameObject tooltipPrefab2 = host.TooltipPrefab; if ((Object)(object)tooltipPrefab2 != (Object)null) { UITooltip obj2 = ((Component)_mode).gameObject.AddComponent<UITooltip>(); obj2.m_tooltipPrefab = tooltipPrefab2; obj2.m_text = "$hoard_ui_all_tip"; } float num4 = (width - 24f) / 3f; for (int num5 = 0; num5 < _slots.Length; num5++) { int slot = num5; StatSlot statSlot = new StatSlot { Root = UiKit.NewRect((Transform)(object)_drawer, "stat" + num5) }; UiKit.Place(statSlot.Root, (float)num5 * (num4 + 12f), 38f, num4, 30f); statSlot.Stat = kit.Button((Transform)(object)statSlot.Root, "stat", "", 15f, delegate { OpenStat(slot); }); UiKit.Place((Component)(object)statSlot.Stat, 0f, 0f, num4 - 142f, 30f); statSlot.Cmp = kit.Button((Transform)(object)statSlot.Root, "cmp", "", 16f, delegate { ToggleCmp(slot); }); UiKit.Place((Component)(object)statSlot.Cmp, num4 - 138f, 0f, 40f, 30f); statSlot.Value = kit.Input((Transform)(object)statSlot.Root, "value", UiKit.L("$hoard_ui_value"), 15f, 8); UiKit.Place((Component)(object)statSlot.Value, num4 - 94f, 0f, 64f, 30f); ((UnityEvent<string>)(object)statSlot.Value.onValueChanged).AddListener((UnityAction<string>)delegate(string t) { UiKit.Guard("stat value", delegate { SetValue(slot, t); }); }); statSlot.Remove = kit.Button((Transform)(object)statSlot.Root, "remove", "x", 15f, delegate { RemoveStat(slot); }); UiKit.Place((Component)(object)statSlot.Remove, num4 - 26f, 0f, 26f, 30f); _slots[num5] = statSlot; } _addStat = kit.Button((Transform)(object)_drawer, "addstat", UiKit.L("$hoard_ui_addstat"), 15f, delegate { OpenStat(-1); }); UiKit.Place((Component)(object)_addStat, 0f, 38f, 110f, 30f); ((Component)_drawer).gameObject.SetActive(false); } public IEnumerable<TMP_InputField> Inputs() { yield return _search; StatSlot[] slots = _slots; foreach (StatSlot statSlot in slots) { yield return statSlot.Value; } } public void SetDrawer(bool open) { if (DrawerOpen != open) { DrawerOpen = open; ((Component)_drawer).gameObject.SetActive(open); Render(); _host.DrawerChanged(open); } } public void RestoreDrawer(bool open) { DrawerOpen = open; ((Component)_drawer).gameObject.SetActive(open); } public void Render() { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_05d1: Unknown result type (might be due to invalid IL or missing references) //IL_0576: Unknown result type (might be due to invalid IL or missing references) //IL_056f: Unknown result type (might be due to invalid IL or missing references) Availability avail = _host.Avail; if (_search.text != _spec.Text && !_search.isFocused) { _search.SetTextWithoutNotify(_spec.Text); } bool active = avail.Rarity || _spec.Rarities.Count > 0; for (int i = 0; i < _chips.Length; i++) { Button obj = _chips[i]; ((Component)obj).gameObject.SetActive(active); bool flag = _spec.Rarities.Contains(i); Color val = ElUi.RarityColor(i); UiKit.Tint(obj, flag ? new Color(val.r, val.g, val.b, 1f) : new Color(val.r * 0.45f, val.g * 0.45f, val.b * 0.45f, 0.75f)); TMP_Text val2 = UiKit.Label(obj); if ((Object)(object)val2 != (Object)null) { string text = ElUi.RarityName(i); val2.text = ((text.Length > 0) ? text.Substring(0, Math.Min(2, text.Length)) : (i + 1).ToString(CultureInfo.InvariantCulture)); ((Graphic)val2).color = (flag ? Color.black : Color.white); } } bool active2 = avail.Types.Count > 0 || _spec.Type != null || _spec.Group.HasValue; ((Component)_type).gameObject.SetActive(active2); SetLabel(_type, TypeLabel(), _spec.Type != null || _spec.Group.HasValue); bool active3 = avail.Biomes.Count > 0 || _spec.Biome != null; ((Component)_biome).gameObject.SetActive(active3); SetLabel(_biome, (_spec.Biome == null) ? UiKit.L("$hoard_ui_anybiome") : WindowView.BiomeName(_spec.Biome), _spec.Biome != null); int num = UiLogic.DrawerFilterCount(_spec); SetLabel(_more, (num > 0) ? string.Format("{0} ({1})", UiKit.L("$hoard_ui_more"), num) : UiKit.L("$hoard_ui_more"), DrawerOpen || num > 0); ((Selectable)_clear).interactable = UiLogic.AnyFilter(_spec) || _drafts.Count > 0; if (!DrawerOpen) { return; } _minValue.text = ((_spec.MinQuality > 1) ? _spec.MinQuality.ToString(CultureInfo.InvariantCulture) : "-"); ((Selectable)_minDown).interactable = _spec.MinQuality > 1; bool active4 = avail.Sets || _spec.SetOnly; ((Component)_set).gameObject.SetActive(active4); SetLabel(_set, UiKit.L("$hoard_ui_setonly"), _spec.SetOnly); bool active5 = avail.Effects || _spec.Enchants.Count > 0; ((Component)_enchLabel).gameObject.SetActive(active5); ((Component)_pick).gameObject.SetActive(active5); ((Component)_mode).gameObject.SetActive(active5); SetLabel(_pick, (_spec.Enchants.Count > 0) ? UiLogic.Fill(UiKit.L("$hoard_ui_picked"), _spec.Enchants.Count.ToString(CultureInfo.InvariantCulture)) : UiKit.L("$hoard_ui_pick"), _spec.Enchants.Count > 0); SetLabel(_mode, UiKit.L((_spec.EnchantMode == EnchantMode.All) ? "$hoard_ui_all" : "$hoard_ui_any"), active: false); for (int j = 0; j < _slots.Length; j++) { StatSlot statSlot = _slots[j]; bool flag2 = j < _drafts.Count; ((Component)statSlot.Root).gameObject.SetActive(flag2); if (flag2) { Draft draft = _drafts[j]; SetLabel(statSlot.Stat, UiKit.L(Query.StatToken(draft.Stat)), active: true); SetLabel(statSlot.Cmp, (draft.Cmp == StatCmp.AtLeast) ? ">=" : "<=", active: false); if (!statSlot.Value.isFocused && statSlot.Value.text != draft.Text) { statSlot.Value.SetTextWithoutNotify(draft.Text); } float value; bool flag3 = draft.Text.Length == 0 || UiLogic.TryParseNumber(draft.Text, out value); ((Graphic)statSlot.Value.textComponent).color = (flag3 ? Color.white : UiKit.Warn); } } bool flag4 = _drafts.Count < 3 && AnyStatAvailable(avail); ((Component)_addStat).gameObject.SetActive(flag4); if (flag4) { float x = _slots[0].Root.sizeDelta.x; UiKit.Place((Component)(object)_addStat, (float)_drafts.Count * (x + 12f), 38f, 110f, 30f); } } private static bool AnyStatAvailable(Availability a) { for (int i = 0; i < Row.StatCount; i++) { if (a.Stats[i]) { return true; } } return false; } private static void SetLabel(Button b, string text, bool active) { //IL_0030: 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) TMP_Text val = UiKit.Label(b); if ((Object)(object)val != (Object)null) { if (val.text != text) { val.text = text; } ((Graphic)val).color = (active ? UiKit.Orange : Color.white); } } private string TypeLabel() { if (_spec.Type != null) { if (_spec.Type.Length != 0) { return WindowView.TypeName(_spec.Type); } return UiKit.L("$hoard_unclassified"); } TypeGroup? typeGroup = _spec.Group; if (typeGroup.HasValue) { TypeGroup valueOrDefault = typeGroup.GetValueOrDefault(); return UiKit.L(Query.GroupToken(valueOrDefault)); } return UiKit.L("$hoard_ui_anytype"); } private void Changed() { Render(); _host.FiltersChanged(); } private void ToggleRarity(int r) { if (!_spec.Rarities.Remove(r)) { _spec.Rarities.Add(r); } Changed(); } private void OpenType() { //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Expected O, but got Unknown Availability avail = _host.Avail; List<PopupItem> list = new List<PopupItem> { new PopupItem { Label = UiKit.L("$hoard_ui_anytype"), Selected = (_spec.Type == null && !_spec.Group.HasValue), OnClick = delegate { SetType(null, null); } } }; TypeGroup[] array = new TypeGroup[4] { TypeGroup.Weapons, TypeGroup.Shields, TypeGroup.Armor, TypeGroup.Other }; foreach (TypeGroup g in array) { List<string> list2 = avail.Types.FindAll((string t) => Query.GroupOf(t) == g); string type = _spec.Type; if (type != null && type.Length > 0 && Query.GroupOf(type) == g && !list2.Contains(type)) { list2.Add(type); } if (list2.Count == 0 && _spec.Group != g) { continue; } TypeGroup group = g; list.Add(new PopupItem { Label = UiKit.L(Query.GroupToken(g)), Header = true, Selected = (_spec.Group == g && _spec.Type == null), OnClick = delegate { SetType(group, null); } }); foreach (string item in list2) { string type2 = item; list.Add(new PopupItem { Label = WindowView.TypeName(item), Selected = (_spec.Type == item), OnClick = delegate { SetType(null, type2); } }); } } _host.OpenPopup((RectTransform)((Component)_type).transform, list, 200f); } private void SetType(TypeGroup? group, string? type) { _spec.Group = group; _spec.Type = type; Changed(); } private void OpenBiome() { //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Expected O, but got Unknown Availability avail = _host.Avail; List<PopupItem> list = new List<PopupItem> { new PopupItem { Label = UiKit.L("$hoard_ui_anybiome"), Selected = (_spec.Biome == null), OnClick = delegate { SetBiome(null); } } }; List<string> list2 = new List<string>(avail.Biomes); if (_spec.Biome != null && !list2.Contains(_spec.Biome)) { list2.Add(_spec.Biome); } foreach (string item in list2) { string biome = item; list.Add(new PopupItem { Label = WindowView.BiomeName(item), Selected = (_spec.Biome == item), OnClick = delegate { SetBiome(biome); } }); } _host.OpenPopup((RectTransform)((Component)_biome).transform, list, 180f); } private void SetBiome(string? biome) { _spec.Biome = biome; Changed(); } private void StepMin(int d) { int num = Math.Max(1, _spec.MinQuality); _spec.MinQuality = Mathf.Clamp(num + d, 1, 10); Changed(); } private void ToggleSet() { _spec.SetOnly = !_spec.SetOnly; Changed(); } private void ToggleMode() { _spec.EnchantMode = ((_spec.EnchantMode == EnchantMode.All) ? EnchantMode.Any : EnchantMode.All); Changed(); } private void OpenStat(int slot) { //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Expected O, but got Unknown Availability avail = _host.Avail; List<PopupItem> list = new List<PopupItem>(); for (int i = 0; i < Row.StatCount; i++) { Stat s = (Stat)i; bool flag = slot >= 0 && slot < _drafts.Count && _drafts[slot].Stat == s; if (avail.Stats[i] || flag) { list.Add(new PopupItem { Label = UiKit.L(Query.StatToken(s)), Selected = flag, OnClick = delegate { SetStat(slot, s); } }); } } if (list.Count != 0) { Button val = ((slot >= 0) ? _slots[slot].Stat : _addStat); _host.OpenPopup((RectTransform)((Component)val).transform, list, 170f); } } private void SetStat(int slot, Stat s) { if (slot < 0 || slot >= _drafts.Count) { if (_drafts.Count >= 3) { return; } _drafts.Add(new Draft { Stat = s, Cmp = ((s == Stat.Weight) ? StatCmp.AtMost : StatCmp.AtLeast) }); } else { _drafts[slot].Stat = s; } Apply(); } private void ToggleCmp(int slot) { if (slot < _drafts.Count) { Draft draft = _drafts[slot]; draft.Cmp = ((draft.Cmp == StatCmp.AtLeast) ? StatCmp.AtMost : StatCmp.AtLeast); Apply(); } } private void SetValue(int slot, string text) { if (slot < _drafts.Count) { _drafts[slot].Text = text ?? ""; Apply(); } } private void RemoveStat(int slot) { if (slot < _drafts.Count) { _drafts.RemoveAt(slot); Apply(); } } private void Apply() { _spec.StatRows.Clear(); foreach (Draft draft in _drafts) { if (UiLogic.TryParseNumber(draft.Text, out var value)) { _spec.StatRows.Add(new StatRow(draft.Stat, draft.Cmp, UiLogic.InputToRow(draft.Stat, value))); } } Changed(); } private void ClearAll() { UiLogic.ClearFilters(_spec); _drafts.Clear(); _search.SetTextWithoutNotify(""); Changed(); } } internal static class GuiBinds { private static FieldRef<InventoryGui, GameObject>? _dragGo; private static FieldRef<InventoryGui, ItemData>? _dragItem; private static FieldRef<InventoryGui, Inventory>? _dragInventory; private static FieldRef<InventoryGui, int>? _dragAmount; private static Action<InventoryGui, ItemData?, Inventory?, int>? _setupDragItem; private static Action<InventoryGui, int, bool>? _setActiveGroup; private static FieldRef<InventoryGui, int>? _activeGroup; private static FieldRef<Humanoid, ItemData>? _hiddenRight; private static FieldRef<Humanoid, ItemData>? _hiddenLeft; internal static MethodInfo HideMi; internal static MethodInfo? OnSelectedItemMi; internal static MethodInfo? UpdateMi; internal static bool GroupsBound { get { if (_setActiveGroup != null) { return _activeGroup != null; } return false; } } internal static string? Bind() { try { _dragGo = Hoard.Bind.FieldRef<InventoryGui, GameObject>("m_dragGo"); _dragItem = Hoard.Bind.FieldRef<InventoryGui, ItemData>("m_dragItem"); _dragInventory = Hoard.Bind.FieldRef<InventoryGui, Inventory>("m_dragInventory"); _dragAmount = Hoard.Bind.FieldRef<InventoryGui, int>("m_dragAmount"); _setupDragItem = AccessTools.MethodDelegate<Action<InventoryGui, ItemData, Inventory, int>>(Hoard.Bind.Method(typeof(InventoryGui), "SetupDragItem", new Type[3] { typeof(ItemData), typeof(Inventory), typeof(int) }, typeof(void)), (object)null, true); HideMi = Hoard.Bind.Method(typeof(InventoryGui), "Hide", Type.EmptyTypes, typeof(void)); } catch (Exception ex) { return Log.Innermost(ex); } OnSelectedItemMi = Optional("Ctrl-click deposit", () => Hoard.Bind.Method(typeof(InventoryGui), "OnSelectedItem", new Type[4] { typeof(InventoryGrid), typeof(ItemData), typeof(Vector2i), typeof(Modifier) }, typeof(void))); UpdateMi = Optional("gamepad control of the list", () => Hoard.Bind.Method(typeof(InventoryGui), "Update", Type.EmptyTypes, typeof(void))); Optional("gamepad focus between the list and the inventory", delegate { MethodInfo methodInfo = Hoard.Bind.Method(typeof(InventoryGui), "SetActiveGroup", new Type[2] { typeof(int), typeof(bool) }, typeof(void)); _setActiveGroup = AccessTools.MethodDelegate<Action<InventoryGui, int, bool>>(methodInfo, (object)null, true); _activeGroup = Hoard.Bind.FieldRef<InventoryGui, int>("m_activeGroup"); return methodInfo; }); Optional("Deposit all's sheathed-weapon check", delegate { _hiddenRight = Hoard.Bind.FieldRef<Humanoid, ItemData>("m_hiddenRightItem"); _hiddenLeft = Hoard.Bind.FieldRef<Humanoid, ItemData>("m_hiddenLeftItem"); return (MethodInfo?)null; }); return null; } private static MethodInfo? Optional(string what, Func<MethodInfo?> bind) { try { return bind(); } catch (Exception