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 ThorsKist v0.8.6
plugins/ThorsKist.dll
Decompiled 3 weeks 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.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Splatform; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("posixone")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+8fa289af510fd71e7f51dd6727e5ddfc9b53b31d")] [assembly: AssemblyProduct("ThorsKist")] [assembly: AssemblyTitle("ThorsKist")] [assembly: AssemblyVersion("1.0.0.0")] namespace ThorsKist; public static class ItemWire { private const int InventoryVersion = 109; public static void WriteItems(ZPackage pkg, List<ItemData> items) { pkg.Write(109); pkg.Write((ushort)items.Count); for (int i = 0; i < items.Count; i++) { items[i].Save(pkg); } } public static void WriteItem(ZPackage pkg, ItemData item) { pkg.Write(109); pkg.Write((ushort)1); item.Save(pkg); } public static List<ItemData> ReadItems(ZPackage pkg) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) Inventory val = new Inventory("tk_scratch", (Sprite)null, 8, 256); val.Load(pkg); return new List<ItemData>(val.GetAllItems()); } } public enum ItemCategory { Weapon, Ammo, Armor, Tool, Food, Seed, Wood, Stone, Ore, Hide, Parts, Fibre, Gem, Trophy, Fish, Valuable, Other } public static class KistCategories { public static readonly string[] Names = new string[17] { "Weapon", "Ammo", "Armour", "Tool", "Food", "Seeds", "Wood", "Stone", "Ore & Metal", "Hides", "Parts", "Fibre", "Gems & Cores", "Trophy", "Fish", "Valuables", "Other" }; public static readonly string[] ExpectedItemTypes = new string[23] { "Material=1", "Consumable=2", "OneHandedWeapon=3", "Bow=4", "Shield=5", "Helmet=6", "Chest=7", "Ammo=9", "Customization=10", "Legs=11", "Hands=12", "Trophy=13", "TwoHandedWeapon=14", "Torch=15", "Misc=16", "Shoulder=17", "Utility=18", "Tool=19", "Attach_Atgeir=20", "Fish=21", "TwoHandedWeaponLeft=22", "AmmoNonEquipable=23", "Trinket=24" }; public static string NameOf(ItemCategory c) { if (c < ItemCategory.Weapon || (int)c >= Names.Length) { return ""; } return Names[(int)c]; } public static bool FromItemType(int itemType, out ItemCategory category) { switch (itemType) { case 3: case 4: case 14: case 20: case 22: category = ItemCategory.Weapon; return true; case 9: case 23: category = ItemCategory.Ammo; return true; case 5: case 6: case 7: case 10: case 11: case 12: case 17: case 18: case 24: category = ItemCategory.Armor; return true; case 15: case 19: category = ItemCategory.Tool; return true; case 2: category = ItemCategory.Food; return true; case 13: category = ItemCategory.Trophy; return true; case 21: category = ItemCategory.Fish; return true; default: category = ItemCategory.Other; return false; } } public static ItemCategory FromToken(string token) { if (string.IsNullOrEmpty(token)) { return ItemCategory.Other; } string haystack = token.ToLowerInvariant(); if (Has(haystack, "seed", "acorn", "pinecone", "sapling", "cone")) { return ItemCategory.Seed; } if (Has(haystack, "wood", "log", "bark", "branch", "stake", "beech", "birch")) { return ItemCategory.Wood; } if (Has(haystack, "hide", "pelt", "leather", "scale", "carapace", "chitin", "fur")) { return ItemCategory.Hide; } if (Has(haystack, "flax", "linen", "jute", "thread", "cloth", "yarn", "string")) { return ItemCategory.Fibre; } if (Has(haystack, "berry", "berries", "mushroom", "honey", "meat", "carrot", "turnip", "onion", "barley", "flour", "bread", "mead", "potion", "dandelion", "thistle", "sausage", "stew", "soup", "pie", "jam", "cream", "egg", "milk", "fish")) { return ItemCategory.Food; } if (Has(haystack, "bone", "fang", "tusk", "claw", "tooth", "horn", "eye", "gland", "sinew", "tissue", "mandible", "feather", "jelly", "entrail", "bloodbag", "ooze", "guck", "stinger", "wing", "bilebag", "tail", "sack", "heart", "brain", "neck", "trophy")) { return ItemCategory.Parts; } if (Has(haystack, "core", "crystal", "ruby", "amber", "gem", "pearl", "eitr", "sap", "tear", "wisp", "essence")) { return ItemCategory.Gem; } if (Has(haystack, "coin", "treasure", "necklace", "ring")) { return ItemCategory.Valuable; } if (Has(haystack, "stone", "flint", "marble", "obsidian", "grausten", "rock", "clay", "salt", "sulfur")) { return ItemCategory.Stone; } if (Has(haystack, "ore", "metal", "ingot", "bronze", "copper", "tin", "iron", "silver", "flametal", "nail", "chain", "scrap", "steel", "coal")) { return ItemCategory.Ore; } return ItemCategory.Other; } private static bool Has(string haystack, params string[] needles) { for (int i = 0; i < needles.Length; i++) { if (haystack.IndexOf(needles[i], StringComparison.Ordinal) >= 0) { return true; } } return false; } } public static class KistClient { public class RosterEntry { public long PlayerId; public string Name = ""; } private class PendingDeposit { public int Token; public List<ItemData> Items; public float Deadline; } public static Hird Hird; public static Inventory Kist; public static int MinHirdSize = 2; public static bool HasPendingInvite; public static string PendingInviteHird = ""; public static string PendingInviteFrom = ""; public static readonly List<RosterEntry> Roster = new List<RosterEntry>(); public static bool AnchorAllowed; public static string AnchorReason = ""; public static string AnchorCostPrefab = ""; public static int AnchorCostAmount; public static Action OnStateChanged; private static readonly List<PendingDeposit> Pending = new List<PendingDeposit>(); private static int _nextToken = 1; private static float _nextHello; private static bool _helloSent; private static float _nextPing; private static float _nextAnchorQuery; public static bool InHird => Hird != null; public static bool KistUnlocked { get { if (Hird != null) { return Hird.Members.Count >= MinHirdSize; } return false; } } public static bool IsOwner { get { Player localPlayer = Player.m_localPlayer; if (Hird != null && (Object)(object)localPlayer != (Object)null) { return Hird.OwnerId == localPlayer.GetPlayerID(); } return false; } } public static float DistanceToAnchor { get { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (Hird == null || (Object)(object)localPlayer == (Object)null) { return float.MaxValue; } return Vector3.Distance(((Component)localPlayer).transform.position, Hird.Anchor); } } public static bool CanDeposit { get { if (KistUnlocked) { return InRange(Main.DepositRadius.Value); } return false; } } public static bool CanTake { get { if (KistUnlocked) { return InRange(Main.TakeRadius.Value); } return false; } } public static bool CanCraft { get { if (Main.CraftFromKist.Value && KistUnlocked) { return InRange(Main.CraftRadius.Value); } return false; } } public static bool InRange(float radius) { if (Main.UnlimitedRange.Value) { return true; } return DistanceToAnchor <= radius; } public static void Reset() { Hird = null; Kist = null; Roster.Clear(); Pending.Clear(); HasPendingInvite = false; AnchorAllowed = false; AnchorReason = ""; _helloSent = false; _nextHello = 0f; } private static ZPackage NewMessage(Wire.C2S op) { //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_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown Player localPlayer = Player.m_localPlayer; ZPackage val = new ZPackage(); val.Write(4); val.Write((byte)op); val.Write(((Object)(object)localPlayer != (Object)null) ? localPlayer.GetPlayerID() : 0); val.Write(((Object)(object)localPlayer != (Object)null) ? localPlayer.GetPlayerName() : ""); val.Write(((Object)(object)localPlayer != (Object)null) ? ((Component)localPlayer).transform.position : Vector3.zero); return val; } private static void Send(ZPackage pkg) { if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC("ThorsKist_C2S", new object[1] { pkg }); } } public static void RequestState() { Send(NewMessage(Wire.C2S.RequestState)); } public static void CreateHird(string name) { ZPackage obj = NewMessage(Wire.C2S.CreateHird); obj.Write(name ?? ""); Send(obj); } public static void Invite(long playerId, string name) { ZPackage obj = NewMessage(Wire.C2S.Invite); obj.Write(playerId); obj.Write(name ?? ""); Send(obj); } public static void AcceptInvite() { Send(NewMessage(Wire.C2S.AcceptInvite)); } public static void DeclineInvite() { Send(NewMessage(Wire.C2S.DeclineInvite)); } public static void Leave() { Send(NewMessage(Wire.C2S.Leave)); } public static void Disband() { Send(NewMessage(Wire.C2S.Disband)); } public static void SetAnchorHere() { Send(NewMessage(Wire.C2S.SetAnchor)); } public static void QueryAnchor() { Send(NewMessage(Wire.C2S.AnchorQuery)); } public static void RenameHird(string name) { ZPackage obj = NewMessage(Wire.C2S.Rename); obj.Write(name ?? ""); Send(obj); } public static int AnchorCostHeld() { if (string.IsNullOrEmpty(AnchorCostPrefab) || AnchorCostAmount <= 0) { return int.MaxValue; } string text = SharedNameOf(AnchorCostPrefab); Player localPlayer = Player.m_localPlayer; if (text == null || (Object)(object)localPlayer == (Object)null) { return 0; } return ((Humanoid)localPlayer).GetInventory().CountItems(text, -1, true); } public static string AnchorCostLabel() { if (string.IsNullOrEmpty(AnchorCostPrefab) || AnchorCostAmount <= 0) { return ""; } string text = SharedNameOf(AnchorCostPrefab); string text2 = ((text != null && Localization.instance != null) ? Localization.instance.Localize(text) : AnchorCostPrefab); return AnchorCostAmount + " " + text2; } private static string SharedNameOf(string prefabName) { if ((Object)(object)ObjectDB.instance == (Object)null) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefabName); if ((Object)(object)itemPrefab == (Object)null) { return null; } ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); if (!((Object)(object)component != (Object)null)) { return null; } return component.m_itemData.m_shared.m_name; } public static void Kick(long playerId) { ZPackage obj = NewMessage(Wire.C2S.Kick); obj.Write(playerId); Send(obj); } public static bool Deposit(Inventory from, ItemData item, int amount) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown if (item == null || from == null) { return false; } if (amount <= 0 || amount > item.m_stack) { amount = item.m_stack; } ItemData val = item.Clone(); val.m_stack = amount; from.RemoveItem(item, amount); int num = _nextToken++; Pending.Add(new PendingDeposit { Token = num, Items = new List<ItemData> { val }, Deadline = Time.realtimeSinceStartup + 15f }); ZPackage obj = NewMessage(Wire.C2S.Deposit); obj.Write(num); ZPackage val2 = new ZPackage(); ItemWire.WriteItems(val2, new List<ItemData> { val }); obj.Write(val2); Send(obj); return true; } public static void Take(Vector2i slot, string expectName, int amount) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) ZPackage obj = NewMessage(Wire.C2S.Take); obj.Write(_nextToken++); obj.Write(slot); obj.Write(expectName ?? ""); obj.Write(amount); Send(obj); } public static void ReportDiag(string text) { if (!string.IsNullOrEmpty(text)) { Main.Log.LogInfo((object)("ThorsKist: " + text)); ZPackage obj = NewMessage(Wire.C2S.Diag); obj.Write(text); Send(obj); } } public static void ReportCraftConsume(List<CraftDraw> draws) { if (draws == null || draws.Count == 0) { return; } ZPackage val = NewMessage(Wire.C2S.ConsumeForCraft); val.Write(draws.Count); foreach (CraftDraw draw in draws) { val.Write(draw.Name); val.Write(draw.Amount); val.Write(draw.Quality); } Send(val); } public static void OnMessage(long sender, ZPackage pkg) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) try { if (pkg.ReadInt() != 4) { Message("Thor's Kist: version mismatch with the server."); return; } switch ((Wire.S2C)pkg.ReadByte()) { case Wire.S2C.State: ReadState(pkg); break; case Wire.S2C.Toast: Message(pkg.ReadString()); break; case Wire.S2C.Denied: Message(pkg.ReadString()); break; case Wire.S2C.DepositAck: ClearPending(pkg.ReadInt()); break; case Wire.S2C.DepositRejected: RollBack(pkg.ReadInt(), pkg.ReadString()); break; case Wire.S2C.AnchorSet: OnAnchorSet(pkg.ReadVector3(), pkg.ReadLong()); break; case Wire.S2C.DepositFx: KistFx.DepositAt(pkg.ReadVector3()); break; case Wire.S2C.AnchorStatus: { bool num = pkg.ReadBool(); string text = pkg.ReadString(); string text2 = pkg.ReadString(); int num2 = pkg.ReadInt(); bool flag = num != AnchorAllowed || text != AnchorReason || text2 != AnchorCostPrefab || num2 != AnchorCostAmount; AnchorAllowed = num; AnchorReason = text; AnchorCostPrefab = text2; AnchorCostAmount = num2; if (flag) { KistUI.MarkDirty(); } break; } case Wire.S2C.TakeResult: ReceiveTake(pkg.ReadInt(), pkg.ReadPackage()); break; } } catch (Exception ex) { Main.Log.LogError((object)("ThorsKist: bad reply from the server: " + ex)); } } private static void ReadState(ZPackage pkg) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown if (pkg.ReadBool()) { Hird = Hird.ReadHeader(pkg); MinHirdSize = pkg.ReadInt(); ZPackage val = pkg.ReadPackage(); if (Kist == null) { Kist = new Inventory("Kist", (Sprite)null, 8, 256); } Kist.Load(val); HasPendingInvite = false; } else { Hird = null; Kist = null; HasPendingInvite = pkg.ReadBool(); if (HasPendingInvite) { PendingInviteHird = pkg.ReadString(); PendingInviteFrom = pkg.ReadString(); } } Roster.Clear(); int num = pkg.ReadInt(); for (int i = 0; i < num; i++) { RosterEntry rosterEntry = new RosterEntry(); rosterEntry.PlayerId = pkg.ReadLong(); rosterEntry.Name = pkg.ReadString(); Roster.Add(rosterEntry); } if (OnStateChanged != null) { OnStateChanged(); } } private static void OnAnchorSet(Vector3 pos, long payerPlayerId) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) KistFx.Anchor(pos); Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && payerPlayerId != 0L && payerPlayerId == localPlayer.GetPlayerID() && !string.IsNullOrEmpty(AnchorCostPrefab) && AnchorCostAmount > 0) { string text = SharedNameOf(AnchorCostPrefab); if (text != null) { ((Humanoid)localPlayer).GetInventory().RemoveItem(text, AnchorCostAmount, -1, true); Message("The Kist took " + AnchorCostLabel() + "."); } } } private static void ClearPending(int token) { Pending.RemoveAll((PendingDeposit p) => p.Token == token); } private static void RollBack(int token, string why) { PendingDeposit pendingDeposit = Pending.Find((PendingDeposit x) => x.Token == token); Pending.RemoveAll((PendingDeposit x) => x.Token == token); if (pendingDeposit != null) { GiveBack(pendingDeposit.Items); } if (!string.IsNullOrEmpty(why)) { Message(why); } } private static void ReceiveTake(int token, ZPackage payload) { List<ItemData> list = ItemWire.ReadItems(payload); if (list.Count != 0) { GiveBack(list); KistFx.Take(); } } private static void GiveBack(List<ItemData> items) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } Inventory inventory = ((Humanoid)localPlayer).GetInventory(); foreach (ItemData item in items) { if (inventory.CanAddItem(item, item.m_stack)) { inventory.AddItem(item); continue; } DropAtFeet(localPlayer, item); Message("$msg_yourinventoryisfull"); } } private static void DropAtFeet(Player p, ItemData item) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((Component)p).transform.position + ((Component)p).transform.forward * 1.5f + Vector3.up; ItemDrop.DropItem(item, item.m_stack, val, Quaternion.identity); } public static void Message(string text) { if (!string.IsNullOrEmpty(text)) { if ((Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true); } else { Main.Log.LogInfo((object)("ThorsKist: " + text)); } } } public static void Tick() { KistServer.Tick(); if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null) { if (_helloSent) { Reset(); } return; } if (!_helloSent && Time.realtimeSinceStartup >= _nextHello) { _nextHello = Time.realtimeSinceStartup + 2f; if (ZRoutedRpc.instance != null && Player.m_localPlayer.GetPlayerID() != 0L) { Send(NewMessage(Wire.C2S.Hello)); _helloSent = true; } } if (InHird && Time.realtimeSinceStartup >= _nextPing) { _nextPing = Time.realtimeSinceStartup + 3f; Send(NewMessage(Wire.C2S.Ping)); } if (InHird && KistUI.WantsAnchorStatus && Time.realtimeSinceStartup >= _nextAnchorQuery) { _nextAnchorQuery = Time.realtimeSinceStartup + 1.5f; QueryAnchor(); } for (int num = Pending.Count - 1; num >= 0; num--) { if (!(Time.realtimeSinceStartup < Pending[num].Deadline)) { List<ItemData> items = Pending[num].Items; Pending.RemoveAt(num); GiveBack(items); Message("The Kist did not answer. Your items were returned."); } } } } public class CraftDraw { public string Name; public int Amount; public int Quality = -1; } public static class KistConflicts { public enum Stake { Display, Deposit, Payment } public sealed class Conflict { public string Mod; public string Method; public bool Replaces; public Stake Stake; public string Key => Mod + "|" + Method; } private static readonly List<Conflict> _found = new List<Conflict>(); private static readonly HashSet<string> _seen = new HashSet<string>(); private static int _scans; private static bool _sawPlayer; private static bool _told; public static bool AnyWorthTelling { get { foreach (Conflict item in _found) { if (item.Stake != Stake.Display) { return true; } } return false; } } public static IList<Conflict> All => _found; public static void Reset() { _told = false; } public static void Tick() { if (_scans == 0) { _scans++; Scan(); return; } if (!_sawPlayer && (Object)(object)Player.m_localPlayer != (Object)null) { _sawPlayer = true; Scan(); } if (!_told && AnyWorthTelling && !((Object)(object)Player.m_localPlayer == (Object)null)) { _told = true; KistDialog.Show("THOR'S KIST - SHARED HOOKS", Report()); } } private static IEnumerable<MethodBase> Seams() { Harmony harmonyInstance = Main.HarmonyInstance; if (harmonyInstance == null) { yield break; } IEnumerable<MethodBase> enumerable; try { enumerable = harmonyInstance.GetPatchedMethods(); } catch (Exception ex) { Main.Log.LogWarning((object)("ThorsKist: could not list our own patches: " + ex.Message)); enumerable = new MethodBase[0]; } foreach (MethodBase item in enumerable) { if (!(item == null) && !(item.DeclaringType == null)) { Type declaringType = item.DeclaringType; if (declaringType == typeof(Inventory) || declaringType == typeof(Player) || declaringType == typeof(InventoryGui)) { yield return item; } } } } private static Stake StakeOf(MethodBase m) { switch (m.Name) { case "CountItems": case "RemoveItem": case "ConsumeResources": case "HaveItem": case "GetItem": return Stake.Payment; case "DoCrafting": return Stake.Display; case "OnSelectedItem": return Stake.Deposit; default: return Stake.Display; } } private static void Scan() { int num = 0; foreach (MethodBase item in Seams()) { num++; string text = ((item.DeclaringType != null) ? (item.DeclaringType.Name + ".") : "") + item.Name; Patches patchInfo; try { patchInfo = Harmony.GetPatchInfo(item); } catch (Exception ex) { Main.Log.LogWarning((object)("ThorsKist: could not read patches on " + text + ": " + ex.Message)); continue; } if (patchInfo == null) { continue; } Stake stake = StakeOf(item); foreach (Patch prefix in patchInfo.Prefixes) { if (!(prefix.owner == "posixone_ThorsKist")) { MethodInfo patchMethod = prefix.PatchMethod; if (!(patchMethod == null) && !(patchMethod.ReturnType != typeof(bool))) { Note(prefix, text, stake, replaces: true); } } } foreach (Patch transpiler in patchInfo.Transpilers) { if (!(transpiler.owner == "posixone_ThorsKist")) { Note(transpiler, text, stake, replaces: false); } } } Main.Log.LogInfo((object)("ThorsKist: conflict scan looked at " + num + " crafting seam(s), found " + _found.Count + " conflict(s).")); } private static void Note(Patch p, string method, Stake stake, bool replaces) { Conflict conflict = new Conflict { Mod = ModName(p), Method = method, Replaces = replaces, Stake = stake }; if (_seen.Add(conflict.Key)) { _found.Add(conflict); string text = "ThorsKist: " + conflict.Mod + (replaces ? " may take over " : " rewrites ") + method; switch (stake) { case Stake.Payment: Main.Log.LogWarning((object)(text + " -- if it does, the Kist can be counted toward a cost and never charged for it.")); break; case Stake.Deposit: Main.Log.LogWarning((object)(text + " -- if it does, an Alt-click deposit may not reach the Kist. Crafting is unaffected.")); break; default: Main.Log.LogInfo((object)(text + " -- Kist amounts on screen may be wrong; payment is unaffected.")); break; } } } private static string ModName(Patch p) { string owner = p.owner; try { foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos) { if (pluginInfo.Value != null && pluginInfo.Value.Metadata != null && (pluginInfo.Key == owner || pluginInfo.Value.Metadata.GUID == owner)) { return pluginInfo.Value.Metadata.Name; } } } catch { } try { if (p.PatchMethod != null && p.PatchMethod.DeclaringType != null) { string name = p.PatchMethod.DeclaringType.Assembly.GetName().Name; if (!string.IsNullOrEmpty(name)) { return name; } } } catch { } if (!string.IsNullOrEmpty(owner)) { return owner; } return "another mod"; } public static string Report() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("Another mod hooks the same game code Thor's Kist uses.\n"); stringBuilder.Append("<i>This is a heads-up, not a fault. It may never affect you.</i>\n"); Section(stringBuilder, Stake.Payment, "Could affect crafting", "If it takes over, the Kist may be counted toward a recipe and never charged for it. Setting <b>craftFromKist = false</b> stops the Kist offering materials it cannot charge for."); Section(stringBuilder, Stake.Deposit, "Could affect depositing", "If it takes over, an Alt-click may not reach the Kist. Crafting is unaffected, and the Kist window still deposits normally."); stringBuilder.Append("\nThe log line <b>\"Kist paid ...\"</b> appears whenever the vault actually pays, "); stringBuilder.Append("so you can always tell a real problem from materials simply coming from elsewhere."); return stringBuilder.ToString(); } private static void Section(StringBuilder sb, Stake stake, string heading, string consequence) { bool flag = false; foreach (Conflict item in _found) { if (item.Stake == stake) { if (!flag) { sb.Append("\n<b>").Append(heading).Append("</b>\n"); flag = true; } sb.Append(" ").Append(item.Mod).Append(' ') .Append(item.Replaces ? "may take over " : "rewrites ") .Append(item.Method) .Append('\n'); } } if (flag) { sb.Append(" ").Append(consequence).Append('\n'); } } } public static class KistDialog { private static GameObject _root; private static TMP_FontAsset _font; private static string _pendingTitle; private static string _pendingBody; private static readonly Color PanelBg = new Color(0.07f, 0.065f, 0.06f, 0.98f); private static readonly Color Parchment = new Color(0.87f, 0.82f, 0.7f, 1f); private static readonly Color Warn = new Color(0.86f, 0.62f, 0.26f, 1f); private static readonly Color BtnBg = new Color(0.24f, 0.21f, 0.16f, 1f); public static bool IsOpen { get { if ((Object)(object)_root != (Object)null) { return _root.activeInHierarchy; } return false; } } private static float S => Mathf.Clamp(Main.UiScale.Value, 0.6f, 1.6f); public static void Show(string title, string body) { _pendingTitle = title; _pendingBody = body; } public static void Tick() { if ((Object)(object)Player.m_localPlayer == (Object)null) { if ((Object)(object)_root != (Object)null) { Close(); } return; } if ((Object)(object)_root == (Object)null && _pendingBody != null) { Build(); } if (IsOpen && (Input.GetKeyDown((KeyCode)27) || Input.GetKeyDown((KeyCode)13) || Input.GetKeyDown((KeyCode)271))) { Close(); } } public static void Close() { if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); } _root = null; _pendingTitle = null; _pendingBody = null; } private static void Build() { //IL_0063: 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_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Expected O, but got Unknown //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)InventoryGui.instance == (Object)null) { return; } Canvas componentInParent = ((Component)InventoryGui.instance).GetComponentInParent<Canvas>(); if (!((Object)(object)componentInParent == (Object)null)) { Transform transform = ((Component)(((Object)(object)componentInParent.rootCanvas != (Object)null) ? componentInParent.rootCanvas : componentInParent)).transform; _font = FindFont(); RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null); float num; if (!((Object)(object)val != (Object)null)) { num = Screen.width; } else { Rect rect = val.rect; num = ((Rect)(ref rect)).width; } float num2 = num; float num3 = Mathf.Round(16f * S); float num4 = Mathf.Min(700f * S, num2 * 0.7f); float size = 18f * S; float size2 = 13f * S; float num5 = Mathf.Round(30f * S); float num6 = Mathf.Round(26f * S); _root = NewPanel("ThorsKist_Dialog", transform, PanelBg); _root.transform.SetAsLastSibling(); TMP_Text val2 = NewText(_root.transform, _pendingTitle ?? "THOR'S KIST", size, Warn, (TextAlignmentOptions)513); TMP_Text obj = NewText(_root.transform, _pendingBody ?? "", size2, Parchment, (TextAlignmentOptions)257); obj.textWrappingMode = (TextWrappingModes)1; obj.overflowMode = (TextOverflowModes)0; float num7 = num4 - num3 * 2f; float y = obj.GetPreferredValues(obj.text, num7, 0f).y; float num8 = num3 + num6 + num3 * 0.5f + y + num3 + num5 + num3; Transform transform2 = _root.transform; Transform obj2 = ((transform2 is RectTransform) ? transform2 : null); Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(0.5f, 0.5f); ((RectTransform)obj2).anchorMax = val3; ((RectTransform)obj2).anchorMin = val3; ((RectTransform)obj2).pivot = new Vector2(0.5f, 0.5f); ((RectTransform)obj2).sizeDelta = new Vector2(num4, num8); ((RectTransform)obj2).anchoredPosition = Vector2.zero; AddOutline(_root, Warn); Place(val2.rectTransform, new Vector2(0f, 1f), new Vector2(num3, 0f - num3), new Vector2(num7, num6)); Place(obj.rectTransform, new Vector2(0f, 1f), new Vector2(num3, 0f - (num3 + num6 + num3 * 0.5f)), new Vector2(num7, y)); Transform transform3 = ((Component)NewButton(_root.transform, "OK", 13f * S, new UnityAction(Close))).transform; Place((RectTransform)(object)((transform3 is RectTransform) ? transform3 : null), new Vector2(0.5f, 0f), new Vector2(0f, num3), new Vector2(Mathf.Round(90f * S), num5), new Vector2(0.5f, 0f)); } } private static TMP_FontAsset FindFont() { if ((Object)(object)InventoryGui.instance != (Object)null) { TMP_Text componentInChildren = ((Component)InventoryGui.instance).GetComponentInChildren<TMP_Text>(true); if ((Object)(object)componentInChildren != (Object)null && (Object)(object)componentInChildren.font != (Object)null) { return componentInChildren.font; } } return null; } private static GameObject NewPanel(string name, Transform parent, Color bg) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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_0047: 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) //IL_0060: Expected O, but got Unknown GameObject val = new GameObject(name, new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image) }); val.transform.SetParent(parent, false); Image component = val.GetComponent<Image>(); ((Graphic)component).color = bg; ((Graphic)component).raycastTarget = bg.a > 0.01f; return val; } private static void AddOutline(GameObject go, Color colour) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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) //IL_0032: Unknown result type (might be due to invalid IL or missing references) Outline obj = go.AddComponent<Outline>(); ((Shadow)obj).effectColor = new Color(colour.r, colour.g, colour.b, 0.55f); ((Shadow)obj).effectDistance = new Vector2(2f, -2f); } private static TMP_Text NewText(Transform parent, string text, float size, Color colour, TextAlignmentOptions align) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: 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_005e: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("text", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(parent, false); TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>(); if ((Object)(object)_font != (Object)null) { ((TMP_Text)val2).font = _font; } ((TMP_Text)val2).text = text; ((TMP_Text)val2).fontSize = size; ((Graphic)val2).color = colour; ((TMP_Text)val2).alignment = align; ((TMP_Text)val2).richText = true; ((Graphic)val2).raycastTarget = false; return (TMP_Text)(object)val2; } private static Button NewButton(Transform parent, string label, float size, UnityAction onClick) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) GameObject val = NewPanel("button", parent, BtnBg); Button obj = val.AddComponent<Button>(); ((Selectable)obj).targetGraphic = (Graphic)(object)val.GetComponent<Image>(); ColorBlock colors = ((Selectable)obj).colors; ((ColorBlock)(ref colors)).normalColor = Color.white; ((ColorBlock)(ref colors)).highlightedColor = new Color(1.25f, 1.25f, 1.25f, 1f); ((ColorBlock)(ref colors)).pressedColor = new Color(0.8f, 0.8f, 0.8f, 1f); ((Selectable)obj).colors = colors; ((UnityEvent)obj.onClick).AddListener(onClick); RectTransform rectTransform = NewText(val.transform, label, size, Parchment, (TextAlignmentOptions)514).rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.offsetMin = new Vector2(2f, 1f); rectTransform.offsetMax = new Vector2(-2f, -1f); return obj; } private static void Place(RectTransform rt, Vector2 anchor, Vector2 offset, Vector2 size) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) Place(rt, anchor, offset, size, new Vector2(anchor.x, anchor.y)); } private static void Place(RectTransform rt, Vector2 anchor, Vector2 offset, Vector2 size, Vector2 pivot) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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) Vector2 anchorMin = (rt.anchorMax = anchor); rt.anchorMin = anchorMin; rt.pivot = pivot; rt.sizeDelta = size; rt.anchoredPosition = offset; } } public static class KistFx { private static bool _warnedDepositSfx; private static bool _warnedDepositVfx; private static bool _warnedAnchorSfx; private static bool _warnedAnchorVfx; private static bool _warnedAnchorFall; public static void Deposit() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { DepositAt(((Character)localPlayer).GetCenterPoint()); } } public static void DepositAt(Vector3 pos) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (Main.DepositSound.Value) { Spawn(Main.DepositSfxPrefab.Value, pos, Main.DepositVolume.Value, ref _warnedDepositSfx); } if (Main.DepositVfx.Value) { Spawn(Main.DepositVfxPrefab.Value, pos, 1f, ref _warnedDepositVfx); } } public static void Take() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) if (Main.TakeSound.Value) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && ((Humanoid)localPlayer).m_pickupEffects != null && ((Humanoid)localPlayer).m_pickupEffects.HasEffects()) { Amplify(((Humanoid)localPlayer).m_pickupEffects.Create(((Component)localPlayer).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)), Main.TakeVolume.Value); } } } public static void Anchor(Vector3 groundPos) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (Main.AnchorEffects.Value && !((Object)(object)Main.Instance == (Object)null)) { ((MonoBehaviour)Main.Instance).StartCoroutine(Slam(groundPos)); } } private static IEnumerator Slam(Vector3 groundPos) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) float height = Mathf.Max(1f, Main.AnchorDropHeight.Value); float fall = Mathf.Max(0.05f, Main.AnchorDropSeconds.Value); GameObject falling = Spawn(Main.AnchorFallVfxPrefab.Value, groundPos + Vector3.up * height, 1f, ref _warnedAnchorFall); float t = 0f; while (t < fall) { t += Time.deltaTime; if ((Object)(object)falling != (Object)null) { float num = Mathf.Clamp01(t / fall); falling.transform.position = Vector3.Lerp(groundPos + Vector3.up * height, groundPos, num * num); } yield return null; } if ((Object)(object)falling != (Object)null) { Object.Destroy((Object)(object)falling); } Spawn(Main.AnchorVfxPrefab.Value, groundPos, 1f, ref _warnedAnchorVfx); Spawn(Main.AnchorSfxPrefab.Value, groundPos, Main.AnchorVolume.Value, ref _warnedAnchorSfx); } private static GameObject Spawn(string prefabName, Vector3 pos, float volume, ref bool warned) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(prefabName)) { return null; } if ((Object)(object)ZNetScene.instance == (Object)null) { return null; } GameObject prefab = ZNetScene.instance.GetPrefab(prefabName); if ((Object)(object)prefab == (Object)null) { if (!warned) { warned = true; Main.Log.LogWarning((object)("ThorsKist: no prefab named '" + prefabName + "'. Check the Feedback section of the config.")); } return null; } GameObject obj = Object.Instantiate<GameObject>(prefab, pos, Quaternion.identity); Amplify(obj, volume); return obj; } private static void Amplify(GameObject[] objects, float volume) { if (objects != null) { for (int i = 0; i < objects.Length; i++) { Amplify(objects[i], volume); } } } private static void Amplify(GameObject go, float volume) { if (!((Object)(object)go == (Object)null) && !Mathf.Approximately(volume, 1f)) { ZSFX[] componentsInChildren = go.GetComponentsInChildren<ZSFX>(true); foreach (ZSFX obj in componentsInChildren) { obj.SetVolumeModifier(obj.GetVolumeModifier() * volume); } } } } public static class KistMap { private static PinData _pin; private static Vector3 _pinnedAt; private static string _pinnedName; private static float _nextSync; private static bool _removingOurOwn; private static Sprite _icon; private static bool _iconResolved; public static bool IsProtected(PinData pin) { if (!_removingOurOwn && pin != null) { return pin == _pin; } return false; } public static void Forget() { _pin = null; } public static void Tick() { if (!(Time.realtimeSinceStartup < _nextSync)) { _nextSync = Time.realtimeSinceStartup + 1f; Sync(); } } public static void Sync() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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_00ab: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Minimap.instance == (Object)null) { _pin = null; return; } if (!Main.MapPin.Value || !KistClient.InHird) { Drop(); return; } Vector3 anchor = KistClient.Hird.Anchor; string name = KistClient.Hird.Name; if (_pin != null && _pinnedAt == anchor && _pinnedName == name) { return; } Drop(); _pin = Minimap.instance.AddPin(anchor, (PinType)3, name, false, false, 0L, default(PlatformUserID)); if (_pin != null) { Sprite val = ChestIcon(); if ((Object)(object)val != (Object)null) { _pin.m_icon = val; } } _pinnedAt = anchor; _pinnedName = name; } private static void Drop() { if (_pin == null) { return; } if ((Object)(object)Minimap.instance != (Object)null) { _removingOurOwn = true; try { Minimap.instance.RemovePin(_pin); } finally { _removingOurOwn = false; } } _pin = null; } private static Sprite ChestIcon() { if (_iconResolved) { return _icon; } if ((Object)(object)ZNetScene.instance == (Object)null) { return null; } _iconResolved = true; string[] array = new string[6] { Main.MapPinPrefab.Value, "piece_chest_wood", "piece_chest_treasure", "piece_chest", "piece_chest_private", "piece_chest_blackmetal" }; foreach (string text in array) { if (string.IsNullOrEmpty(text)) { continue; } GameObject prefab = ZNetScene.instance.GetPrefab(text); if (!((Object)(object)prefab == (Object)null)) { Piece component = prefab.GetComponent<Piece>(); if (!((Object)(object)component == (Object)null) && !((Object)(object)component.m_icon == (Object)null)) { _icon = component.m_icon; Main.Log.LogInfo((object)("ThorsKist: map pin using the icon from '" + text + "'.")); return _icon; } } } Main.Log.LogWarning((object)"ThorsKist: found no chest prefab for the map pin icon; falling back to a plain map pin."); return null; } } public static class KistServer { private class Vault { public Hird Hird; public Inventory Inventory; } private static readonly List<Vault> Vaults = new List<Vault>(); private static readonly Dictionary<long, long> PeerByPlayer = new Dictionary<long, long>(); private static readonly Dictionary<long, string> NameByPlayer = new Dictionary<long, string>(); private static readonly Dictionary<long, Vector3> PosByPlayer = new Dictionary<long, Vector3>(); private static readonly Dictionary<long, float> PosSeenAt = new Dictionary<long, float>(); private static bool _loaded; private static bool _dirty; private static float _nextSave; private const string VaultFolder = "ThorsKist"; public static bool IsServer { get { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } private static string SaveDir { get { string text = Utils.GetSaveDataPath((FileSource)2); if (string.IsNullOrEmpty(text)) { text = Paths.ConfigPath; } return Path.Combine(text, "ThorsKist"); } } private static string LegacySaveDir => Path.Combine(Paths.ConfigPath, "ThorsKist"); private static string VaultFileName => "kist_" + (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetWorldUID() : 0) + ".dat"; private static string SavePath => Path.Combine(SaveDir, VaultFileName); private static void MigrateLegacyVault() { try { string savePath = SavePath; if (!File.Exists(savePath)) { string text = Path.Combine(LegacySaveDir, VaultFileName); if (File.Exists(text)) { Directory.CreateDirectory(SaveDir); File.Copy(text, savePath, overwrite: false); File.Delete(text); Main.Log.LogWarning((object)("ThorsKist: moved the vault out of BepInEx/config, which server managers wipe on update.\n from: " + text + "\n to: " + savePath)); } } } catch (Exception ex) { Main.Log.LogError((object)("ThorsKist: could not move the old vault file: " + ex)); } } public static void EnsureLoaded() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown if (_loaded || !IsServer || (Object)(object)ObjectDB.instance == (Object)null) { return; } _loaded = true; try { MigrateLegacyVault(); string savePath = SavePath; if (!File.Exists(savePath)) { Main.Log.LogInfo((object)("ThorsKist: no vault file yet at " + savePath)); return; } ZPackage val = new ZPackage(File.ReadAllBytes(savePath)); int num = val.ReadInt(); if (num != 1) { Main.Log.LogError((object)("ThorsKist: vault file is protocol " + num + ", this build speaks " + 1 + ". Refusing to load it.")); return; } int num2 = val.ReadInt(); for (int i = 0; i < num2; i++) { Hird hird = Hird.ReadHeader(val); ZPackage val2 = val.ReadPackage(); Vault vault = new Vault { Hird = hird, Inventory = NewInventory(hird) }; vault.Inventory.Load(val2); Vaults.Add(vault); } Main.Log.LogInfo((object)("ThorsKist: loaded " + Vaults.Count + " hird(s) from " + savePath)); } catch (Exception ex) { Main.Log.LogError((object)("ThorsKist: could not load the vault file: " + ex)); } } private static Inventory NewInventory(Hird hird) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown return new Inventory("Kist_" + hird.Id, (Sprite)null, 8, Mathf.Clamp(Main.KistRows.Value, 4, 256)); } private static void Save() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown if (!IsServer) { return; } try { Directory.CreateDirectory(SaveDir); ZPackage val = new ZPackage(); val.Write(1); val.Write(Vaults.Count); foreach (Vault vault in Vaults) { vault.Hird.WriteHeader(val); ZPackage val2 = new ZPackage(); vault.Inventory.Save(val2); val.Write(val2); } string savePath = SavePath; string text = savePath + ".tmp"; File.WriteAllBytes(text, val.GetArray()); if (File.Exists(savePath)) { File.Delete(savePath); } File.Move(text, savePath); _dirty = false; } catch (Exception ex) { Main.Log.LogError((object)("ThorsKist: could not save the vault file: " + ex)); } } private static void MarkDirty() { _dirty = true; _nextSave = Time.realtimeSinceStartup + 2f; } public static void Tick() { if (IsServer) { EnsureLoaded(); if (_dirty && Time.realtimeSinceStartup >= _nextSave) { Save(); } } } public static void FlushNow() { if (_dirty) { Save(); } } public static void Reset() { FlushNow(); Vaults.Clear(); PeerByPlayer.Clear(); NameByPlayer.Clear(); PosByPlayer.Clear(); PosSeenAt.Clear(); _loaded = false; _dirty = false; } private static Vault VaultOf(long playerId) { foreach (Vault vault in Vaults) { if (vault.Hird.IsMember(playerId)) { return vault; } } return null; } private static Vault VaultById(string hirdId) { foreach (Vault vault in Vaults) { if (vault.Hird.Id == hirdId) { return vault; } } return null; } private static Vault VaultWithInviteFor(long playerId) { foreach (Vault vault in Vaults) { if (vault.Hird.IsInvited(playerId)) { return vault; } } return null; } public static void OnMessage(long senderPeer, ZPackage pkg) { //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) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) if (!IsServer) { return; } EnsureLoaded(); try { if (pkg.ReadInt() != 4) { Deny(senderPeer, "Your Thor's Kist version does not match the server's."); return; } Wire.C2S c2S = (Wire.C2S)pkg.ReadByte(); long num = pkg.ReadLong(); string text = pkg.ReadString(); Vector3 val = pkg.ReadVector3(); if (num != 0L) { PeerByPlayer[num] = senderPeer; if (!string.IsNullOrEmpty(text)) { NameByPlayer[num] = text; } PosByPlayer[num] = val; PosSeenAt[num] = Time.realtimeSinceStartup; } switch (c2S) { case Wire.C2S.Hello: BroadcastRoster(); SendState(num); break; case Wire.C2S.RequestState: SendState(num); break; case Wire.C2S.CreateHird: CreateHird(senderPeer, num, text, val, pkg.ReadString()); break; case Wire.C2S.Invite: Invite(senderPeer, num, text, pkg.ReadLong(), pkg.ReadString()); break; case Wire.C2S.AcceptInvite: AcceptInvite(senderPeer, num, text); break; case Wire.C2S.DeclineInvite: DeclineInvite(num); break; case Wire.C2S.Kick: Kick(senderPeer, num, pkg.ReadLong()); break; case Wire.C2S.Leave: Leave(num); break; case Wire.C2S.Rename: Rename(senderPeer, num, pkg.ReadString()); break; case Wire.C2S.Disband: Disband(senderPeer, num); break; case Wire.C2S.AnchorQuery: SendAnchorStatus(senderPeer, num, val); break; case Wire.C2S.SetAnchor: SetAnchor(senderPeer, num, val); break; case Wire.C2S.Deposit: Deposit(senderPeer, num, text, val, pkg.ReadInt(), pkg.ReadPackage()); break; case Wire.C2S.Take: Take(senderPeer, num, val, pkg.ReadInt(), pkg.ReadVector2i(), pkg.ReadString(), pkg.ReadInt()); break; case Wire.C2S.Diag: Main.Log.LogInfo((object)("ThorsKist [" + text + "] " + pkg.ReadString())); break; case Wire.C2S.ConsumeForCraft: ConsumeForCraft(senderPeer, num, val, pkg); break; case Wire.C2S.Ping: break; } } catch (Exception ex) { Main.Log.LogError((object)("ThorsKist: bad message from peer " + senderPeer + ": " + ex)); } } private static void CreateHird(long peer, long playerId, string playerName, Vector3 pos, string name) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) if (VaultOf(playerId) != null) { Deny(peer, "You already belong to a hird."); return; } if (string.IsNullOrEmpty(name)) { name = playerName + "'s hird"; } if (name.Length > 40) { name = name.Substring(0, 40); } Hird hird = new Hird { Id = Guid.NewGuid().ToString("N"), Name = name, OwnerId = playerId, Anchor = pos }; hird.Members.Add(new HirdMember(playerId, playerName)); Vault vault = new Vault { Hird = hird, Inventory = NewInventory(hird) }; Vaults.Add(vault); MarkDirty(); AnnounceAnchor(vault, 0L); Toast(peer, "Hird \"" + name + "\" founded. Invite at least " + (Main.MinHirdSize.Value - 1) + " more to unlock the Kist."); SendState(playerId); } private static void Invite(long peer, long playerId, string playerName, long targetId, string targetName) { Vault vault = VaultOf(playerId); if (vault == null) { Deny(peer, "You are not in a hird."); return; } if (vault.Hird.OwnerId != playerId) { Deny(peer, "Only the hird's founder may invite."); return; } if (vault.Hird.IsMember(targetId)) { Deny(peer, targetName + " is already in your hird."); return; } if (VaultOf(targetId) != null) { Deny(peer, targetName + " already belongs to another hird."); return; } if (vault.Hird.IsInvited(targetId)) { Deny(peer, targetName + " has already been invited."); return; } vault.Hird.Invites.Add(new HirdInvite { PlayerId = targetId, Name = targetName, FromPlayerId = playerId, FromName = playerName }); MarkDirty(); Toast(peer, "Invited " + targetName + "."); SendStateToHird(vault); } private static void AcceptInvite(long peer, long playerId, string playerName) { Vault vault = VaultWithInviteFor(playerId); if (vault == null) { Deny(peer, "That invitation is no longer open."); return; } if (VaultOf(playerId) != null) { Deny(peer, "You already belong to a hird."); return; } vault.Hird.Invites.RemoveAll((HirdInvite i) => i.PlayerId == playerId); vault.Hird.Members.Add(new HirdMember(playerId, playerName)); MarkDirty(); SendStateToHird(vault); ToastHird(vault, playerName + " has joined the hird."); } private static void DeclineInvite(long playerId) { Vault vault = VaultWithInviteFor(playerId); if (vault != null) { vault.Hird.Invites.RemoveAll((HirdInvite i) => i.PlayerId == playerId); MarkDirty(); SendStateToHird(vault); SendState(playerId); } } private static void Kick(long peer, long playerId, long targetId) { Vault vault = VaultOf(playerId); if (vault == null) { Deny(peer, "You are not in a hird."); return; } if (vault.Hird.OwnerId != playerId) { Deny(peer, "Only the hird's founder may remove members."); return; } if (targetId == playerId) { Deny(peer, "Disband the hird instead."); return; } string text = vault.Hird.MemberName(targetId); vault.Hird.Members.RemoveAll((HirdMember m) => m.PlayerId == targetId); vault.Hird.Invites.RemoveAll((HirdInvite i) => i.PlayerId == targetId); MarkDirty(); ToastHird(vault, text + " was removed from the hird."); SendStateToHird(vault); SendState(targetId); } private static void Leave(long playerId) { Vault vault = VaultOf(playerId); if (vault == null) { return; } if (vault.Hird.OwnerId == playerId) { vault.Hird.Members.RemoveAll((HirdMember m) => m.PlayerId == playerId); if (vault.Hird.Members.Count > 0) { vault.Hird.OwnerId = vault.Hird.Members[0].PlayerId; } } else { vault.Hird.Members.RemoveAll((HirdMember m) => m.PlayerId == playerId); } MarkDirty(); SendStateToHird(vault); SendState(playerId); } private static void Rename(long peer, long playerId, string name) { Vault vault = VaultOf(playerId); if (vault == null) { Deny(peer, "You are not in a hird."); return; } if (vault.Hird.OwnerId != playerId) { Deny(peer, "Only the hird's founder may rename it."); return; } name = (name ?? "").Trim(); if (name.Length == 0) { Deny(peer, "A hird needs a name."); return; } if (name.Length > 40) { name = name.Substring(0, 40); } if (!(name == vault.Hird.Name)) { string name2 = vault.Hird.Name; vault.Hird.Name = name; MarkDirty(); ToastHird(vault, "\"" + name2 + "\" is now \"" + name + "\"."); SendStateToHird(vault); } } private static void Disband(long peer, long playerId) { Vault vault = VaultOf(playerId); if (vault == null) { return; } if (vault.Hird.OwnerId != playerId) { Deny(peer, "Only the hird's founder may disband it."); return; } if (vault.Inventory.NrOfItems() > 0) { Deny(peer, "Empty the Kist before disbanding -- " + vault.Inventory.NrOfItems() + " stack(s) would be destroyed."); return; } List<HirdMember> list = new List<HirdMember>(vault.Hird.Members); Vaults.Remove(vault); MarkDirty(); foreach (HirdMember item in list) { SendState(item.PlayerId); } } private static bool MayReanchor(Vault v, Vector3 at, out string why) { //IL_008c: 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) why = null; int num = 0; foreach (HirdMember member in v.Hird.Members) { if (IsConnected(member.PlayerId)) { num++; if (!PosByPlayer.TryGetValue(member.PlayerId, out var value) || !PosSeenAt.TryGetValue(member.PlayerId, out var value2) || Time.realtimeSinceStartup - value2 > 20f) { why = "Still waiting to hear where " + member.Name + " is."; return false; } float num2 = Vector3.Distance(value, at); if (num2 > Main.AnchorRadius.Value) { why = member.Name + " is " + Mathf.RoundToInt(num2) + "m away. Everyone must be within " + Mathf.RoundToInt(Main.AnchorRadius.Value) + "m."; return false; } } } if (num < Main.MinHirdSize.Value) { why = "The hird must be gathered: " + num + " of " + Main.MinHirdSize.Value + " online."; return false; } return true; } private static void SendAnchorStatus(long peer, long playerId, Vector3 at) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) Vault vault = VaultOf(playerId); ZPackage val = NewReply(Wire.S2C.AnchorStatus); if (vault == null || vault.Hird.OwnerId != playerId) { val.Write(false); val.Write((vault == null) ? "You are not in a hird." : "Only the hird's founder may move the Kist."); } else { string why; bool flag = MayReanchor(vault, at, out why); val.Write(flag); val.Write(flag ? "" : why); } val.Write(Main.AnchorCostPrefab.Value ?? ""); val.Write(Main.AnchorCostAmount.Value); SendToPeer(peer, val); } private static void SetAnchor(long peer, long playerId, Vector3 pos) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) Vault vault = VaultOf(playerId); if (vault == null) { Deny(peer, "You are not in a hird."); return; } if (vault.Hird.OwnerId != playerId) { Deny(peer, "Only the hird's founder may move the Kist."); return; } if (!MayReanchor(vault, pos, out var why)) { Deny(peer, why); return; } vault.Hird.Anchor = pos; MarkDirty(); ToastHird(vault, "The Kist has been anchored to a new location."); AnnounceAnchor(vault, playerId); SendStateToHird(vault); } private static bool Usable(Vault v, Vector3 pos, float radius, out string why) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) why = null; if (v.Hird.Members.Count < Main.MinHirdSize.Value) { why = "The Kist stays shut until the hird has " + Main.MinHirdSize.Value + " members."; return false; } if (Main.UnlimitedRange.Value) { return true; } float num = Vector3.Distance(pos, v.Hird.Anchor); if (num > radius) { why = "Too far from the Kist (" + Mathf.RoundToInt(num) + "m of " + Mathf.RoundToInt(radius) + "m)."; return false; } return true; } private static void Deposit(long peer, long playerId, string playerName, Vector3 pos, int token, ZPackage items) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) Vault vault = VaultOf(playerId); if (vault == null) { Reject(peer, token, "You are not in a hird."); return; } if (!Usable(vault, pos, Main.DepositRadius.Value, out var why)) { Reject(peer, token, why); return; } List<ItemData> list = ItemWire.ReadItems(items); if (list.Count == 0) { Reject(peer, token, "Nothing to store."); return; } foreach (ItemData item in list) { if (!vault.Inventory.CanAddItem(item, item.m_stack)) { Reject(peer, token, "The Kist is full."); return; } } foreach (ItemData item2 in list) { vault.Inventory.AddItem(item2); } MarkDirty(); Ack(peer, token); AnnounceDeposit(vault, playerId, pos); SendStateToHird(vault); } private static void Take(long peer, long playerId, Vector3 pos, int token, Vector2i slot, string expectName, int amount) { //IL_0018: 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_0042: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Expected O, but got Unknown Vault vault = VaultOf(playerId); if (vault == null) { Reject(peer, token, "You are not in a hird."); return; } if (!Usable(vault, pos, Main.TakeRadius.Value, out var why)) { Reject(peer, token, why); return; } ItemData itemAt = vault.Inventory.GetItemAt(slot.x, slot.y); if (itemAt == null) { Reject(peer, token, "That item is already gone."); return; } if (itemAt.m_shared.m_name != expectName) { Reject(peer, token, "That item is already gone."); return; } if (amount <= 0 || amount > itemAt.m_stack) { amount = itemAt.m_stack; } ItemData val = itemAt.Clone(); val.m_stack = amount; if (amount >= itemAt.m_stack) { vault.Inventory.RemoveItem(itemAt); } else { itemAt.m_stack -= amount; } MarkDirty(); ZPackage val2 = NewReply(Wire.S2C.TakeResult); val2.Write(token); ZPackage val3 = new ZPackage(); ItemWire.WriteItem(val3, val); val2.Write(val3); SendToPeer(peer, val2); SendStateToHird(vault); } private static void ConsumeForCraft(long peer, long playerId, Vector3 pos, ZPackage pkg) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) Vault vault = VaultOf(playerId); if (vault == null) { return; } if (!Usable(vault, pos, Main.CraftRadius.Value, out var why)) { Main.Log.LogWarning((object)("ThorsKist: refused a craft consume from player " + playerId + " -- " + why)); return; } int num = pkg.ReadInt(); bool flag = false; for (int i = 0; i < num; i++) { string text = pkg.ReadString(); int num2 = pkg.ReadInt(); int quality = pkg.ReadInt(); if (num2 > 0) { int num3 = KistStock.Count(vault.Inventory, text, quality); if (num3 < num2) { Main.Log.LogWarning((object)("ThorsKist: craft consume short by " + (num2 - num3) + " x " + text)); num2 = num3; } if (num2 > 0) { KistStock.Take(vault.Inventory, text, num2, quality); flag = true; } } } if (flag) { MarkDirty(); SendStateToHird(vault); } } private static ZPackage NewReply(Wire.S2C op) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(4); val.Write((byte)op); return val; } private static void SendToPeer(long peer, ZPackage pkg) { if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(peer, "ThorsKist_S2C", new object[1] { pkg }); } } private static void SendToPlayer(long playerId, ZPackage pkg) { if (PeerByPlayer.TryGetValue(playerId, out var value)) { SendToPeer(value, pkg); } } private static void Deny(long peer, string message) { ZPackage val = NewReply(Wire.S2C.Denied); val.Write(message); SendToPeer(peer, val); } private static void Toast(long peer, string message) { ZPackage val = NewReply(Wire.S2C.Toast); val.Write(message); SendToPeer(peer, val); } private static void ToastHird(Vault v, string message) { foreach (HirdMember member in v.Hird.Members) { ZPackage val = NewReply(Wire.S2C.Toast); val.Write(message); SendToPlayer(member.PlayerId, val); } } private static void AnnounceAnchor(Vault v, long payerPlayerId) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) foreach (HirdMember member in v.Hird.Members) { ZPackage val = NewReply(Wire.S2C.AnchorSet); val.Write(v.Hird.Anchor); val.Write(payerPlayerId); SendToPlayer(member.PlayerId, val); } } private static void AnnounceDeposit(Vault v, long byPlayerId, Vector3 at) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) foreach (HirdMember member in v.Hird.Members) { if (member.PlayerId != byPlayerId) { ZPackage val = NewReply(Wire.S2C.DepositFx); val.Write(at); SendToPlayer(member.PlayerId, val); } } } private static void Ack(long peer, int token) { ZPackage val = NewReply(Wire.S2C.DepositAck); val.Write(token); SendToPeer(peer, val); } private static void Reject(long peer, int token, string why) { ZPackage val = NewReply(Wire.S2C.DepositRejected); val.Write(token); val.Write(why ?? ""); SendToPeer(peer, val); } private static void SendState(long playerId) { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Expected O, but got Unknown Vault vault = VaultOf(playerId); ZPackage val = NewReply(Wire.S2C.State); if (vault == null) { val.Write(false); Vault vault2 = VaultWithInviteFor(playerId); if (vault2 != null) { val.Write(true); val.Write(vault2.Hird.Name); HirdInvite hirdInvite = vault2.Hird.Invites.Find((HirdInvite i) => i.PlayerId == playerId); val.Write((hirdInvite != null) ? hirdInvite.FromName : ""); } else { val.Write(false); } } else { val.Write(true); vault.Hird.WriteHeader(val); val.Write(Main.MinHirdSize.Value); ZPackage val2 = new ZPackage(); vault.Inventory.Save(val2); val.Write(val2); } WriteRoster(val, playerId); SendToPlayer(playerId, val); } private static void WriteRoster(ZPackage pkg, long forPlayer) { List<KeyValuePair<long, string>> list = new List<KeyValuePair<long, string>>(); foreach (KeyValuePair<long, string> item in NameByPlayer) { if (item.Key != forPlayer && IsConnected(item.Key) && VaultOf(item.Key) == null) { list.Add(item); } } pkg.Write(list.Count); foreach (KeyValuePair<long, string> item2 in list) { pkg.Write(item2.Key); pkg.Write(item2.Value); } } private static bool IsConnected(long playerId) { if (!PeerByPlayer.TryGetValue(playerId, out var value)) { return false; } if ((Object)(object)ZNet.instance == (Object)null) { return false; } if (value == ZNet.GetUID()) { return true; } return ZNet.instance.GetPeer(value) != null; } private static void SendStateToHird(Vault v) { foreach (HirdMember member in v.Hird.Members) { SendState(member.PlayerId); } foreach (HirdInvite invite in v.Hird.Invites) { SendState(invite.PlayerId); } } public static void BroadcastRoster() { foreach (long item in new List<long>(NameByPlayer.Keys)) { if (IsConnected(item)) { SendState(item); } } } } public enum SortMode { Name, Quantity, Progression, Category } public static class KistSort { private struct Key { public ItemData Item; public string Name; public int Tier; public int Cat; public int Total; public int Stack; } public const int UnknownTier = 99; private static readonly string[] TierNames = new string[8] { "Meadows", "Black Forest", "Swamp", "Mountain", "Plains", "Mistlands", "Ashlands", "Deep North" }; private static Dictionary<string, int> _tier; private static Dictionary<string, ItemCategory> _catCache; private static readonly string[][] Seeds = new string[7][] { new string[14] { "Wood", "Stone", "Flint", "Resin", "Feathers", "LeatherScraps", "DeerHide", "Dandelion", "Mushroom", "Raspberry", "Honey", "NeckTail", "DeerMeat", "Coal" }, new string[19] { "RoundLog", "FineWood", "CopperOre", "Copper", "TinOre", "Tin", "Bronze", "SurtlingCore", "GreydwarfEye", "BoneFragments", "Thistle", "Blueberries", "Ruby", "TrollHide", "Carrot", "CarrotSeeds", "Acorn", "BeechSeeds", "PineCone" }, new string[16] { "IronScrap", "Iron", "ElderBark", "Guck", "Chain", "WitheredBone", "Entrails", "Bloodbag", "Ooze", "AncientSeed", "Root", "Turnip", "TurnipSeeds", "Amber", "AmberPearl", "Ironpit" }, new string[12] { "SilverOre", "Silver", "Obsidian", "WolfPelt", "WolfFang", "FreezeGland", "Crystal", "Onion", "OnionSeeds", "JuteRed", "DragonEgg", "DragonTear" }, new string[11] { "BlackMetalScrap", "BlackMetal", "Flax", "Barley", "LinenThread", "BarleyFlour", "Needle", "Tar", "LoxPelt", "Cloudberry", "RoyalJelly" }, new string[16] { "BlackMarble", "YggdrasilWood", "Eitr", "Sap", "Softtissue", "BlackCore", "Wisp", "Carapace", "MechanicalSpring", "ScaleHide", "Mandible", "JuteBlue", "Thunderstone", "GiantBloodSack", "MushroomJotunPuffs", "MushroomMagecap" }, new string[15] { "Flametal", "FlametalNew", "FlametalOreNew", "AskHide", "CharredBone", "MoltenCore", "ProustitePowder", "CelestialFeather", "MorgenSinew", "Grausten", "Blackwood", "Sulfur", "Vineberry", "BonemawTooth", "CharcoalResin" } }; private static readonly KeyValuePair<Biome, int>[] BiomeOrder = new KeyValuePair<Biome, int>[9] { new KeyValuePair<Biome, int>((Biome)1, 0), new KeyValuePair<Biome, int>((Biome)8, 1), new KeyValuePair<Biome, int>((Biome)256, 1), new KeyValuePair<Biome, int>((Biome)2, 2), new KeyValuePair<Biome, int>((Biome)4, 3), new KeyValuePair<Biome, int>((Biome)16, 4), new KeyValuePair<Biome, int>((Biome)512, 5), new KeyValuePair<Biome, int>((Biome)32, 6), new KeyValuePair<Biome, int>((Biome)64, 7) }; public static string ModeLabel(SortMode m) { return m switch { SortMode.Quantity => "Amount", SortMode.Progression => "Progress", SortMode.Category => "Type", _ => "A-Z", }; } public static string RowTag(ItemData item, SortMode mode) { if (item == null) { return ""; } switch (mode) { case SortMode.Progression: { int num = TierOf(item); if (num < 0 || num >= TierNames.Length) { return "unknown"; } return TierNames[num]; } case SortMode.Category: return KistCategories.NameOf(CategoryOf(item)); default: return ""; } } public static List<ItemData> Arrange(List<ItemData> items, SortMode mode) { List<ItemData> list = new List<ItemData>(); if (items == null) { return list; } Dictionary<string, int> dictionary = null; if (mode == SortMode.Quantity) { dictionary = new Dictionary<string, int>(); foreach (ItemData item2 in items) { string text = TokenOf(item2); if (text != null) { dictionary[text] = (dictionary.TryGetValue(text, out var value) ? (value + item2.m_stack) : item2.m_stack); } } } List<Key> list2 = new List<Key>(items.Count); foreach (ItemData item3 in items) { Key item = new Key { Item = item3, Name = DisplayName(item3), Stack = item3.m_stack, Tier = ((mode == SortMode.Progression || mode == SortMode.Category) ? TierOf(item3) : 0), Cat = (int)((mode == SortMode.Progression || mode == SortMode.Category) ? CategoryOf(item3) : ItemCategory.Weapon) }; if (dictionary != null) { string text2 = TokenOf(item3); item.Total = ((text2 != null && dictionary.TryGetValue(text2, out var value2)) ? value2 : item3.m_stack); } list2.Add(item); } list2.Sort(delegate(Key a, Key b) { switch (mode) { case SortMode.Quantity: { int num = b.Total.CompareTo(a.Total); if (num != 0) { return num; } num = ByName(a, b); if (num != 0) { return num; } return b.Stack.CompareTo(a.Stack); } case SortMode.Progression: { int num = a.Tier.CompareTo(b.Tier); if (num != 0) { return num; } num = a.Cat.CompareTo(b.Cat); if (num != 0) { return num; } return ByName(a, b); } case SortMode.Category: { int num = a.Cat.CompareTo(b.Cat); if (num != 0) { return num; } num = a.Tier.CompareTo(b.Tier); if (num != 0) { return num; } return ByName(a, b); } default: return ByName(a, b); } }); foreach (Key item4 in list2) { list.Add(item4.Item); } return list; } private static int ByName(Key a, Key b) { int num = string.Compare(a.Name, b.Name, StringComparison.CurrentCultureIgnoreCase); if (num != 0) { return num; } int num2 = ((a.Item != null) ? a.Item.m_quality : 0); int value = ((b.Item != null) ? b.Item.m_quality : 0); return num2.CompareTo(value); } public static string DisplayName(ItemData item) { if (item == null || item.m_shared == null) { return ""; } if (Localization.instance == null) { return item.m_shared.m_name; } return Localization.instance.Localize(item.m_shared.m_name); } private static string TokenOf(ItemData item) { if (item == null || item.m_shared == null) { return null; } return item.m_shared.m_name; } public static ItemCategory CategoryOf(ItemData item) { if (item == null || item.m_shared == null) { return ItemCategory.Other; } string name = item.m_shared.m_name; if (string.IsNullOrEmpty(name)) { return ItemCategory.Other; } if (_catCache == null) { _catCache = new Dictionary<string, ItemCategory>(); } if (_catCache.TryGetValue(name, out var value)) { return value; } ItemCategory itemCategory = Classify(item, name); _catCache[name] = itemCategory; return itemCategory; } private static ItemCategory Classify(ItemData item, string token) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected I4, but got Unknown if (KistCategories.FromItemType((int)item.m_shared.m_itemType, out var category)) { return category; } return KistCategories.FromToken(token); } public static int TierOf(ItemData item) { string text = TokenOf(item); if (string.IsNullOrEmpty(text)) { return 99; } EnsureTiers(); if (_tier == null || !_tier.TryGetValue(text, out var value)) { return 99; } return value; } public static void Reset() { _tier = null; _catCache = null; } private static void EnsureTiers() { if (_tier == null && !((Object)(object)ObjectDB.instance == (Object)null)) { _tier = new Dictionary<string, int>(); int num = Guarded("seed", SeedPass); int num2 = Guarded("vegetation", VegetationPass); int num3 = Guarded("creatures", CreaturePass); int num4 = Guarded("recipes", RecipePass); Main.Log.LogInfo((object)("ThorsKist: progression table built -- " + _tier.Count + " items (" + num + " seeded, " + num2 + " from world growth, " + num3 + " from creature drops, " + num4 + " from recipes).")); } } private static int Guarded(string what, Func<int> pass) { try { return pass(); } catch (Exception ex) { Main.Log.LogWarning((object)("ThorsKist: progression pass '" + what + "' failed, items it would have covered will sort as unknown. " + ex.Message)); return 0; } } private static bool Note(string token, int tier) { if (string.IsNullOrEmpty(token) || tier < 0 || tier >= TierNames.Length) { return false; } if (_tier.TryGetValue(token, out var value)) { if (tier >= value) { return false; } _tier[token] = tier; return false; } _tier[token] = tier; return true; } private static int SeedPass() { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { return 0; } int num = 0; List<string> list = null; for (int i = 0; i < Seeds.Length; i++) { string[] array = Seeds[i]; foreach (string text in array) { string text2 = TokenOf(instance.GetItemPrefab(text)); if (text2 == null) { if (list == null) { list = new List<string>(); } list.Add(text); } else if (Note(text2, i)) { num++; } } } if (list != null) { Main.Log.LogInfo((object)("ThorsKist: " + list.Count + " seeded prefab names no longer resolve (" + string.Join(", ", list.ToArray()) + "). They will sort as unknown.")); } return num; } private static int VegetationPass() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)instance == (Object)null || instance.m_vegetation == null) { return 0; } int num = 0; foreach (ZoneVegetation item in instance.m_vegetation) { if (item != null && !((Object)(object)item.m_prefab == (Object)null)) { int num2 = BiomeTier(item.m_biome); if (num2 >= 0) { num += HarvestFrom(item.m_prefab, num2); } } } return num; } private static int HarvestFrom(GameObject prefab, int tier) { int num = 0; Pickable[] componentsInChildren = prefab.GetComponentsInChildren<Pickable>(true); foreach (Pickable val in componentsInChildren) { if ((Object)(object)val != (Object)null && Note(TokenOf(val.m_itemPrefab), tier)) { num++; } } DropOnDestroyed[] componentsInChildren2 = prefab.GetComponentsInChildren<DropOnDestroyed>(true); foreach (DropOnDestroyed val2 in componentsInChildren2) { if ((Object)(object)val2 != (Object)null) { num += FromTable(val2.m_dropWhenDestroyed, tier); } } MineRock[] componentsInChildren3 = prefab.GetComponentsInChildren<MineRock>(true); foreach (MineRock val3 in componentsInChildren3) { if ((Object)(object)val3 != (Object)null) { num += FromTable(val3.m_dropItems, tier); } } MineRock5[] componentsInChildren4 = prefab.GetComponentsInChildren<MineRock5>(true); foreach (MineRock5 val4 in componentsInChildren4) { if ((Object)(object)val4 != (Object)null) { num += FromTable(val4.m_dropItems, tier); } } return num; } private static int FromTable(DropTable table, int tier) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) if (table == null || table.m_drops == null) { return 0; } int num = 0; foreach (DropData drop in table.m_drops) { if (Note(TokenOf(drop.m_item), tier)) { num++; } } return num; } private static int CreaturePass() { //IL_00f5: Unknown result type (might be due to invalid IL or missing references) SpawnSystem[] array = Object.FindObjectsOfType<SpawnSystem>(true); if (array == null) { return 0; } List<SpawnSystemList> list = new List<SpawnSystemList>(); SpawnSystem[] array2 = array; foreach (SpawnSystem val in array2) { if ((Object)(object)val == (Object)null || val.m_spawnLists == null) { continue; } foreach (SpawnSystemList spawnList in val.m_spawnLists) { if ((Object)(object)spawnList != (Object)null && !list.Contains(spawnList)) { list.Add(spawnList); } } } int num = 0; foreach (SpawnSystemList item in list) { if ((Object)(object)item == (Object)null || item.m_spawners == null) { continue; } foreach (SpawnData spawner in item.m_spawners) { if (spawner == null || (Object)(object)spawner.m_prefab == (Object)null) { continue; } int num2 = BiomeTier(spawner.m_biome); if (num2 < 0) { continue; } CharacterDrop[] componentsInChildren = spawner.m_prefab.GetComponentsInChildren<CharacterDrop>(true); foreach (CharacterDrop val2 in componentsInChildren) { if ((Object)(object)val2 == (Object)null || val2.m_drops == null) { continue; } foreach (Drop drop in val2.m_drops) { if (Note(TokenOf(drop.m_prefab), num2)) { num++; } } } } } return num; } private static int RecipePass() { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null || instance.m_recipes == null) { return 0; } int num = 0; for (int i = 0; i < 8; i++) { bool flag = false; foreach (Recipe recipe in instance.m_recipes) { if ((Object)(object)recipe == (Object)null || !recipe.m_enabled || (Object)(object)recipe.m_item == (Object)null) { continue; } string text = TokenOf(((Component)recipe.m_item).gameObject); if (text == null) { continue; } int num2 = StationTier(recipe.m_craftingStation); bool flag2 = num2 >= 0; if (recipe.m_resources != null) { Requirement[] resources = recipe.m_resources; foreach (Requirement val in resources) { if (val == null || (Object)(object)val.m_resItem == (Object)null) { continue; } string text2 = TokenOf(((Component)val.m_resItem).gameObject); if (text2 != null && _tier.TryGetValue(text2, out var value)) { if (value > num2) { num2 = value; } flag2 = true; } } } if (!flag2 || num2 < 0) { continue; } if (_tier.TryGetValue(text, out var value2)) { if (num2 >= value2) { continue; } } else { num++; } _tier[text] = num2; flag = true; } if (!flag) { break; } } return num; } private static int StationTier(CraftingStation station) { if ((Object)(object)station == (Object)null) { return -1; } string text = ((((Object)station).name != null) ? ((Object)station).name.ToLowerInvariant() : ""); if (text.Contains("blackforge")) { return 5; } if (text.Contains("magetable") || text.Contains("galdr")) { return 5; } if (text.Contains("artisan")) { return 4; } if (text.Contains("forge")) { return 1; } if (text.Contains("stonecutter")) { return 2; } if (text.Contains("workbench") || text.Contains("cauldron") || text.Contains("preptable")) { return 0; } return -1; } private static string TokenOf(GameObject go) { if ((Object)(object)go == (Object)null) { return null; } ItemDrop component = go.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null) { return null; } return component.m_itemData.m_shared.m_name; } private static int BiomeTier(Biome biome) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) int num = -1; KeyValuePair<Biome, int>[] biomeOrder = BiomeOrder; for (int i = 0; i < biomeOrder.Length; i++) { KeyValuePair<Biome, int> keyValuePair = biomeOrder[i]; if ((biome & keyValuePair.Key) != 0 && (num < 0 || keyValuePair.Value < num)) { num = keyValuePair.Value; } } return num; } } public static class StationScope { private static int _depth; private static int _commit; public static string Label = ""; public static bool Active { get { if (_depth > 0) { return CraftContext.Active; } return false; } } public static bool Committing { get { if (_commit > 0) { return Active; } return false; } } public static string State => "depth=" + _depth + " commit=" + _commit + " craftActive=" + CraftContext.Active + " canCraft=" + KistClient.CanCraft; public static void Enter(bool committing, string label = "?") { if (_depth == 0) { Label = label; } else { Label = Label + ">" + label; } _depth++; if (committing) { _commit++; } CraftContext.Push(); } public static void Exit(bool committing) { CraftContext.Pop(); if (committing && _commit > 0) { _commit--; } if (_depth > 0) { _depth--; } if (_depth == 0) { CraftContext.FlushDraws(); } } public static void FrameReset() { if (_depth != 0) { Main.Log.LogWarning((object)("ThorsKist: station scope was left open (depth " + _depth + ") -- a station call threw. Forcing it shut.")); while (_depth > 0) { CraftContext.Pop(); _depth--; } _commit = 0; CraftContext.FlushDraws(); } } } [HarmonyPatch] public static class Patches_StationScope { [HarmonyPatch(typeof(Smelter), "OnAddOre")] [HarmonyPrefix] [HarmonyPriority(800)] public static void SmelterOrePre() { StationScope.Enter(committing: true, "Smelter.OnAddOre"); } [HarmonyPatch(typeof(Smelter), "OnAddOre")] [HarmonyPostfix] public static void SmelterOrePost() { StationScope.Exit(committing: true); } [HarmonyPatch(typeof(Smelter), "OnAddFuel")] [HarmonyPrefix] [HarmonyPriority(800)] public static void SmelterFuelPre() { StationScope.Enter(committing: true, "Smelter.OnAddFuel"); } [HarmonyPatch(typeof(Smelter), "OnAddFuel")] [HarmonyPostfix] public static void SmelterFuelPost() { StationScope.Exit(committing: true); } [HarmonyPatch(typeof(Smelter), "CanUseItems")] [HarmonyPrefix] [HarmonyPriority(800)] public static void SmelterCanPre() { StationScope.Enter(committing: false, "Smelter.CanUseItems"); } [HarmonyPatch(typeof(Smelter), "CanUseItems")] [HarmonyPostfix] public static void SmelterCanPost() { StationScope.Exit(committing: false); } [HarmonyPatch(typeof(Fireplace), "Interact")] [HarmonyPrefix] [HarmonyPriority(800)] public static void FireplaceInteractPre() { StationScope.Enter(committing: true, "Fireplace.Interact"); } [HarmonyPatch(typeof(Fireplace), "Interact")] [HarmonyPostfix] public static void FireplaceInteractPost() { StationScope.Exit(committing: true); } [HarmonyPatch(typeof(Fireplace), "UseItem")] [HarmonyPrefix] [HarmonyPriority(800)] public static void FireplaceUsePre() { StationScope.Enter(committing: true, "Fireplace.UseItem"); } [HarmonyPatch(typeof(Fireplace), "UseItem")] [HarmonyPostfix] public static void FireplaceUsePost() { StationScope.Exit(committing: true); } [HarmonyPatch(typeof(Fireplace), "CanUseItems")] [HarmonyPrefix] [HarmonyPriority(800)] public static void FireplaceCanPre() { StationScope.Enter(committing: false, "Fireplace.CanUseItems"); } [HarmonyPatch(typeof(Fireplace), "CanUseItems")] [HarmonyPostfix] public static void FireplaceCanPost() { StationScope.Exit(committing: false); } [HarmonyPatch(typeof(CookingStation), "OnAddFuelSwitch")] [HarmonyPrefix] [HarmonyPriority(800)] public static void CookFuelPre() { StationScope.Enter(committing: true, "CookingStation.OnAddFuelSwitch"); } [HarmonyPatch(typeof(CookingStation), "OnAddFuelSwitch")] [HarmonyPostfix] public static void CookFuelPost() { StationScope.Exit(committing: true); } [HarmonyPatch(typeof(CookingStation), "OnAddFoodSwitch")] [HarmonyPrefix] [HarmonyPriority(800)] public static void CookFoodPre() { StationScope.Enter(committing: true, "CookingStation.OnAddFoodSwitch"); } [HarmonyPatch(typeof(CookingStation), "OnAddFoodSwitch")] [HarmonyPostfix] public static void CookFoodPost() { StationScope.Exit(committing: true); } [HarmonyPatch(typeof(CookingStation), "UseItem")] [HarmonyPrefix] [HarmonyPriority(800)] public static void CookUsePre() { StationScope.Enter(committing: true, "CookingStation.UseItem"); } [HarmonyPatch(typeof(CookingStation), "UseItem")] [HarmonyPostfix] public static void CookUsePost() { StationScope.Exit(committing: true); } [HarmonyPatch(typeof(CookingStation), "CanUseItems")] [HarmonyPrefix] [HarmonyPriority(800)] public static void CookCanPre() { StationScope.Enter(committing: false, "CookingStation.CanUseItems"); } [HarmonyPatch(typeof(CookingStation), "CanUseItems")] [HarmonyPostfix] public static void CookCanPost() { StationScope.Exit(committing: false); } [HarmonyPatch(typeof(CookingStation), "OnInteract")] [HarmonyPrefix] [HarmonyPriority(800)] public static void CookInteractPre() { StationScope.Enter(committing: true, "CookingStation.OnInteract"); } [HarmonyPatch(typeof(CookingStation), "OnInteract")] [HarmonyPostfix] public static void CookInteractPost() { StationScope.Exit(committing: true); } [HarmonyPatch(typeof(Fermenter), "Interact")] [HarmonyPrefix] [HarmonyPriority(800)] public static void FermentInteractPre() { StationScope.Enter(committing: true, "Fermenter.Interact"); } [HarmonyPatch(typeof(Fermenter), "Interact")] [HarmonyPostfix] public static void FermentInteractPost() { StationScope.Exit(committing: true); } [HarmonyPatch(typeof(Fermenter), "AddItem")] [HarmonyPrefix] [HarmonyPriority(800)] public static void FermentAddPre() { StationScope.Enter(committing: true, "Fermenter.AddItem"); } [HarmonyPatch(typeof(Fermenter), "AddItem")] [HarmonyPostfix] public static void FermentAddPost() { StationScope.Exit(committing: true); } [HarmonyPatch(typeof(Fermenter), "UseItem")] [HarmonyPrefix] [HarmonyPriority(800)] public static void FermentUsePre() { StationScope.Enter(committing: true, "Fermenter.UseItem"); } [HarmonyPatch(typeof(Fermenter), "UseItem")] [HarmonyPostfix] public static void FermentUsePost() { StationScope.Exit(committing: true); } } [HarmonyPatch(typeof(Inventory), "HaveItem", new Type[] { typeof(string), typeof(bool) })] public static class Patches_HaveItem { [HarmonyPostfix] public static void Postfix(Inventory __instance, string name, ref bool __result) { if (__result || !StationScope.Active || !CraftContext.IsPlayerInventory(__instance)) { return; } Inventory kist = KistClient.Kist; if (kist == null) { return; } CraftContext.EnterRaw(); try { if (KistStock.Count(kist, name, -1) > 0) { __result = true; } } finally { CraftContext.ExitRaw(); } } } [HarmonyPatch(typeof(Inventory), "CountItemsByName")] public static class Patches_CountItemsByName { [HarmonyPostfix] public static void Postfix(Inventory __instance, string[] names, int quality, ref int __result) { if (!StationScope.Active || !CraftContext.IsPlayerInventory(__instance) || names == null) { return; } Inventory kist = KistClient.Kist; if (kist == null) { return; } CraftContext.EnterRaw(); try { foreach (string text in names) { if (!string.IsNullOrEmpty(text)) { __result += KistStock.Count(kist, text, quality); } } } finally { CraftContext.ExitRaw(); } } } [HarmonyPatch(typeof(Inventory), "GetItem", new Type[] { typeof(string), typeof(int), typeof(bool) })] public static class Patches_GetItem { [HarmonyPostfix] public static void Postfix(Inventory __instance, string name, int quality, bool isPrefabName, ref ItemData __result) { if (__result != null || !StationScope.Active || isPrefabName || !CraftContext.IsPlayerInventory(__instance)) { return; } Inventory kist = KistClient.Kist; if (kist == null) { return; } bool committing = StationScope.Committing; string state = StationScope.State; CraftContext.EnterRaw(); try { ItemData val = KistStock.Find(kist, name, quality); if (val == null) { if (committing) { KistClient.ReportDiag("station[" + StationScope.Label + "] wanted " + name + " and the Kist holds none that match. " + state); } return; } if (!committing) { ItemData val2 = val.Clone(); val2.m_stack = 1; __result = val2; return; } ItemData val3 = val.Clone(); val3.m_stack = 1; if (!__instance.CanAddItem(val3, 1)) { KistClient.Message("$msg_yourinventoryisfull"); KistClient.ReportDiag("station[" + StationScope.Label + "] could not take " + name + " -- the player's inventory is full."); } else if (!__instance.AddItem(val3)) { KistClient.ReportDiag("station[" + StationScope.Label + "] could not take " + name + " -- AddItem refused it."); } else { KistStock.Take(kist, name, 1, quality); CraftContext.Record(name, 1, quality); Main.Log.LogInfo((object)("ThorsKist: station[" + StationScope.Label + "] took 1 x " + name + " from the Kist.")); __result = __instance.GetItem(name, quality, false); } } finally { CraftContext.ExitRaw(); } } } internal static class KistStock { public static int Count(Inventory kist, string name, int quality) { if (kist == null || string.IsNullOrEmpty(name)) { return 0; } return kist.CountItems(name, quality, false); } public static void Take(Inventory kist, string name, int amount, int quality) { if (kist != null && amount > 0) { kist.RemoveItem(name, amount, quality, false); } } public static ItemData Find(Inventory kist, string name, int quality) { if (kist == null || string.IsNullOrEmpty(name)) { return null; } foreach (ItemData allItem in kist.GetAllItems()) { if (allItem != null && allItem.m_shared != null && !(allItem.m_shared.m_name != name) && (quality < 0 || allItem.m_quality == quality)) { return allItem; } } return null; } } public static class KistUI { private enum Tab { Kist, Hird } [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static UnityAction <>9__71_0; public static UnityAction <>9__71_1; public static UnityAction<string> <>9__71_2; public static UnityAction<string> <>9__79_0; public static UnityAction <>9__79_1; public static UnityAction<string> <>9__80_0; public static UnityAction <>9__80_1; internal void <Build>b__71_0() { _tab = Tab.Kist; _dirty = true; } internal void <Build>b__71_1() { _tab = Tab.Hird; _dirty = true; } internal void <Build>b__71_2(string <p0>) { _dirty = true; } internal void <BuildHirdRows>b__79_0(string v) { _hirdNameDraft = v; } internal void <BuildHirdRows>b__79_1() { KistClient.CreateHird(_hirdNameDraft); _hirdNameDraft = ""; } internal void <AddRenameRow>b__80_0(string v) { _renameDraft = v; } internal void <AddRenameRow>b__80_1() { KistClient.RenameHird(_renameDraft); } } public static bool IsOpen; private static GameObject _root; private static RectTransform _content; private static RectTransform _hirdContent; private static ScrollRect _kistScroll; private static ScrollRect _hirdScroll; private static GameObject _kistPage; private static GameObject _hirdPage; private static TMP_Text _title; private static TMP_Text _status; private static TMP_Text _countLabel; private static TMP_InputField _search; private static TMP_InputField _hirdName; private static string _hirdNameDraft = ""; private static TMP_InputField _renameField; private static string _renameDraft = ""; private static string _renameSeededFrom; private static Button _tabKist; private static Button _tabHird; private static GameObject _sortBar; private static readonly List<Button> _sortButtons = new List<Button>(); private static readonly SortMode[] SortOrder = new SortMode[4] { SortMode.Name, SortMode.Quantity, SortMode.Progression, SortMode.Category }; private static TMP_FontAsset _font; private static Tab _tab = Tab.Kist; private static bool _dirty = true; private static bool _lastCanTake; private static Vector2 _builtForScreen; private static readonly List<GameObject> _rows = new List<GameObject>(); private const float RowGap = 0f; private static readonly Color Parchment = new Color(0.87f, 0.82f, 0.7f, 1f); private static readonly Color Dim = new Color(0.62f, 0.58f, 0.5f, 1f); private static readonly Color PanelBg = new Color(0.07f, 0.065f, 0.06f, 0.97f); private static readonly Color RowBg = new Color(0.13f, 0.12f, 0.1f, 0.85f); private static readonly Color RowAlt = new Color(0.17f, 0.15f, 0.12f, 0.85f); private static readonly Color Accent = new Color(0.79f, 0.63f, 0.31f, 1f); private static readonly Color BtnBg = new Color(0.24f, 0.21f, 0.16f, 1f); private static readonly Color BtnBgHot = new Color(0.36f, 0.3f, 0.2f, 1f); private static readonly Color Bad = new Color(0.82f, 0.36f, 0.3f, 1f); private static bool _sortReversed; public static bool BlocksGameInput { get { if (IsOpen && (Object)(object)_root != (Object)null) { return _root.activeInHierarchy; } return false; } } public static bool WantsAnchorStatus { get { if (IsOpen && _tab == Tab.Hird) { return KistClient.IsOwner; } return false; } } private static float S => Mathf.Clamp(Main.UiScale.Value, 0.6f, 1.6f); private static float RowH => Mathf.Round(18f * S); private static float FontRow => 12f * S; private static float FontSmall => 10.5f * S; private static float FontHead => 20f * S; private static float I