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 LokisTradingPost v0.5.6
plugins/TradingPost.dll
Decompiled 16 hours 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.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; 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.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("TradingPost")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0")] [assembly: AssemblyProduct("TradingPost")] [assembly: AssemblyTitle("TradingPost")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace TradingPost { public enum AddonKind { BountyBoard, BountyChest, Repair, Anvil, Obliterator, Vault, Hearth, Fortune } public sealed class AddonDef { public string Id; public string Prefab; public string Display; public string Desc; public string UnlockItem; public string[] Bases; public AddonKind Kind; public Color Tint; public string UnlockId; public Func<int> Price; public Piece PieceRef; public string BaseName; } public static class Addons { public static readonly List<AddonDef> Defs = new List<AddonDef>(); public static readonly Dictionary<string, string> SmeltMap = new Dictionary<string, string>(); private static readonly HashSet<string> _local = new HashSet<string>(); private static readonly HashSet<string> KeepScripts = new HashSet<string> { "ZNetView", "Piece", "WearNTear", "Container" }; public static string Key(string unlockId) { return "tp_unlock_" + unlockId; } public static void MarkUnlocked(string unlockId) { if (!string.IsNullOrEmpty(unlockId)) { _local.Add(unlockId); } } public static void ClearLocal() { _local.Clear(); } public static bool IsUnlocked(string unlockId) { if ((Object)(object)ZoneSystem.instance != (Object)null) { if (!_local.Contains(unlockId)) { return ZoneSystem.instance.GetGlobalKey(Key(unlockId)); } return true; } return false; } public static void RefreshAll() { RefreshLabels(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { try { localPlayer.UpdateKnownRecipesList(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Refresh recipes: " + ex.Message)); } try { AccessTools.Method(typeof(Player), "UpdateAvailablePiecesList", (Type[])null, (Type[])null)?.Invoke(localPlayer, null); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Refresh pieces: " + ex2.Message)); } } try { if ((Object)(object)StoreGui.m_instance != (Object)null && StoreGui.IsVisible()) { StoreGui.m_instance.FillList(); } } catch (Exception ex3) { Plugin.Log.LogWarning((object)("Refresh store: " + ex3.Message)); } } public static void Create() { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_0314: Unknown result type (might be due to invalid IL or missing references) //IL_03c1: Unknown result type (might be due to invalid IL or missing references) //IL_03c6: Unknown result type (might be due to invalid IL or missing references) //IL_046b: Unknown result type (might be due to invalid IL or missing references) //IL_0470: Unknown result type (might be due to invalid IL or missing references) //IL_0515: Unknown result type (might be due to invalid IL or missing references) //IL_051a: Unknown result type (might be due to invalid IL or missing references) Defs.Add(new AddonDef { Id = "board", UnlockId = "board", Kind = AddonKind.BountyBoard, Prefab = "TP_BountyBoard", Display = "Trickster's Errands", Desc = "Daily Kill and Gather errands. [E] next errand, [Shift+E] dismiss the selected one.", Bases = new string[2] { "sign", "piece_banner01" }, Tint = new Color(1f, 0.85f, 0.6f), Price = () => Plugin.PriceBoard.Value }); Defs.Add(new AddonDef { Id = "bountychest", UnlockId = "board", Kind = AddonKind.BountyChest, Prefab = "TP_BountyChest", Display = "Errand Drop", Desc = "Put gather items inside, then [Shift+E] to turn in. ALL contents are destroyed on turn-in; only items for today's open errands count.", Bases = new string[1] { "piece_chest_wood" }, Tint = new Color(1f, 0.75f, 0.45f), Price = null }); Defs.Add(new AddonDef { Id = "repair", UnlockId = "repair", Kind = AddonKind.Repair, Prefab = "TP_RepairStation", Display = "Tinker's Bench", Desc = "[E] repairs everything you carry and wear. No shelter needed.", Bases = new string[3] { "forge_ext4", "piece_workbench_ext2", "piece_workbench" }, Tint = new Color(0.65f, 0.85f, 1f), Price = () => Plugin.PriceRepair.Value }); Defs.Add(new AddonDef { Id = "anvil", UnlockId = "anvil", Kind = AddonKind.Anvil, Prefab = "TP_SmeltersAnvil", Display = "Brokk's Borrowed Anvil", Desc = "[E] turns one ore into one bar, [Shift+E] does all of it. No fuel.", Bases = new string[3] { "forge_ext2", "forge_ext1", "forge" }, Tint = new Color(1f, 0.6f, 0.35f), Price = () => Plugin.PriceAnvil.Value }); Defs.Add(new AddonDef { Id = "obliterator", UnlockId = "obliterator", Kind = AddonKind.Obliterator, Prefab = "TP_Obliterator", Display = "Surtur's Pit", Desc = "[Shift+E] turns everything inside into the same number of Coal. Coins and boss items are left alone.", Bases = new string[3] { "piece_chest_blackmetal", "piece_chest", "piece_chest_wood" }, Tint = new Color(0.6f, 0.45f, 0.75f), Price = () => Plugin.PriceObliterator.Value }); Defs.Add(new AddonDef { Id = "vault", UnlockId = "vault", Kind = AddonKind.Vault, Prefab = "TP_GoldVault", Display = "Vault", Desc = "Your own coin balance, stored safely by the world. [E] deposit all coins, [Shift+E] withdraw. Every vault opens the same balance; destroying one loses nothing.", Bases = new string[3] { "piece_chest_private", "piece_chest_blackmetal", "piece_chest" }, Tint = new Color(1f, 0.85f, 0.3f), Price = () => Plugin.PriceVault.Value }); Defs.Add(new AddonDef { Id = "hearth", UnlockId = "hearth", Kind = AddonKind.Hearth, Prefab = "TP_HearthOfComfort", Display = "Trickster's Hearth", Desc = "Its own comfort source: base comfort plus 1 for each Loki's Stall add-on station nearby. Stacks with fires, beds and so on. It gives no heat: you still need a real fire to rest.", Bases = new string[2] { "hearth", "fire_pit" }, Tint = new Color(1f, 0.7f, 0.55f), Price = () => Plugin.PriceHearth.Value }); Defs.Add(new AddonDef { Id = "fortune", UnlockId = "fortune", Kind = AddonKind.Fortune, Prefab = "TP_FortuneBowl", Display = "Seer's Cauldron", Desc = "[E] with a Fortune Token in your bag: Loki reveals one item from his next stock.", Bases = new string[2] { "piece_cauldron", "piece_chest_wood" }, Tint = new Color(0.6f, 0.8f, 1f), Price = () => Plugin.PriceFortune.Value }); foreach (AddonDef def in Defs) { try { CreatePiece(def); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Add-on piece failed: " + def.Prefab + ": " + ex)); } if (def.Price != null) { try { CreateUnlockItem(def); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Unlock item failed: " + def.Id + ": " + ex2)); } } } string[] array = new string[2] { "smelter", "blastfurnace" }; foreach (string text in array) { try { GameObject prefab = PrefabManager.Instance.GetPrefab(text); Smelter val = ((prefab != null) ? prefab.GetComponent<Smelter>() : null); if (val?.m_conversion == null) { Plugin.Log.LogWarning((object)("Anvil: vanilla " + text + " missing")); continue; } foreach (ItemConversion item in val.m_conversion) { if ((Object)(object)item?.m_from != (Object)null && (Object)(object)item.m_to != (Object)null && !SmeltMap.ContainsKey(((Object)item.m_from).name)) { SmeltMap[((Object)item.m_from).name] = ((Object)item.m_to).name; } } } catch (Exception ex3) { Plugin.Log.LogWarning((object)("Anvil conversions from " + text + ": " + ex3.Message)); } } Plugin.Log.LogInfo((object)("Add-ons: " + Defs.Count((AddonDef d) => (Object)(object)d.PieceRef != (Object)null) + " pieces, anvil knows " + SmeltMap.Count + " ores (" + string.Join(", ", SmeltMap.Keys) + ")")); } private static void CreatePiece(AddonDef d) { //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: 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) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Expected O, but got Unknown //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown //IL_029e: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) string text = d.Bases.FirstOrDefault((string b) => (Object)(object)PrefabManager.Instance.GetPrefab(b) != (Object)null); if (text != null && text != d.Bases[0]) { Plugin.Log.LogWarning((object)("Add-on " + d.Prefab + ": preferred base " + d.Bases[0] + " missing, using " + text)); } if (text == null) { Plugin.Log.LogWarning((object)("Add-on: no vanilla base for " + d.Prefab + " (" + string.Join(", ", d.Bases) + ")")); return; } PieceConfig val = new PieceConfig { Name = d.Display, Description = d.Desc, PieceTable = PieceTables.Hammer, Category = PieceCategories.Misc, CraftingStation = "" }; CustomPiece val2 = new CustomPiece(d.Prefab, text, val); GameObject piecePrefab = val2.PiecePrefab; bool flag = d.Kind == AddonKind.BountyChest || d.Kind == AddonKind.Obliterator; for (int num = 0; num < 4; num++) { foreach (MonoBehaviour item in piecePrefab.GetComponentsInChildren<MonoBehaviour>(true).Reverse()) { if (!((Object)(object)item == (Object)null)) { string name = ((object)item).GetType().Name; if ((!((Object)(object)((Component)item).gameObject == (Object)(object)piecePrefab) || !KeepScripts.Contains(name) || !(name != "Container" || flag)) && (d.Kind != AddonKind.BountyBoard || ((object)item).GetType().Namespace == null || (!((object)item).GetType().Namespace.StartsWith("TMPro") && !((object)item).GetType().Namespace.StartsWith("UnityEngine")))) { Object.DestroyImmediate((Object)(object)item); } } } } Piece component = piecePrefab.GetComponent<Piece>(); component.m_name = d.Display; component.m_description = d.Desc; component.m_resources = (Requirement[])(object)new Requirement[0]; ForceVisible(component); if (flag) { Container component2 = piecePrefab.GetComponent<Container>(); if ((Object)(object)component2 != (Object)null) { component2.m_name = d.Display; } } if (d.Kind == AddonKind.Hearth) { component.m_comfort = Mathf.Max(1, Plugin.HearthBase.Value); component.m_comfortGroup = (ComfortGroup)0; piecePrefab.AddComponent<HearthComfort>(); } if (d.Kind == AddonKind.Obliterator) { piecePrefab.AddComponent<ObliteratorNet>(); } if (d.Kind == AddonKind.BountyBoard) { piecePrefab.AddComponent<BoardLabel>(); BoardLabel.Apply(piecePrefab); } if (flag) { piecePrefab.AddComponent<AddonTag>().Kind = d.Kind; } else { piecePrefab.AddComponent<AddonStation>().Kind = d.Kind; } ShopItems.Tint(piecePrefab, d.Tint); PieceManager.Instance.AddPiece(val2); d.PieceRef = component; d.BaseName = d.Display; Plugin.Log.LogInfo((object)("Add-on piece " + d.Prefab + " from " + text)); } public static void ForceVisible(Piece piece) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) piece.m_craftingStation = null; piece.m_enabled = true; piece.m_category = (PieceCategory)0; piece.m_dlc = ""; piece.m_onlyInBiome = (Biome)0; StationExtension[] componentsInChildren = ((Component)piece).GetComponentsInChildren<StationExtension>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren[i]); } CraftingStation[] componentsInChildren2 = ((Component)piece).GetComponentsInChildren<CraftingStation>(true); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren2[i]); } } public static void AfterPiecesRegistered() { //IL_00ff: Unknown result type (might be due to invalid IL or missing references) PieceTable val = null; try { val = PieceManager.Instance.GetPieceTable(PieceTables.Hammer); } catch { } foreach (AddonDef def in Defs) { try { GameObject prefab = PrefabManager.Instance.GetPrefab(def.Prefab); Piece val2 = (Object.op_Implicit((Object)(object)prefab) ? prefab.GetComponent<Piece>() : null); if ((Object)(object)val2 == (Object)null) { Plugin.Log.LogWarning((object)("Add-on " + def.Prefab + ": no piece prefab")); continue; } ForceVisible(val2); def.PieceRef = val2; bool flag = (Object)(object)val != (Object)null && val.m_pieces.Contains(prefab); if (!flag && (Object)(object)val != (Object)null) { val.m_pieces.Add(prefab); Plugin.Log.LogWarning((object)("Add-on " + def.Prefab + " was missing from the Hammer table; added")); } Plugin.Log.LogInfo((object)string.Format("Add-on {0}: category={1} craftingStation={2} enabled={3} dlc='{4}' inHammer={5} extensions={6}", def.Prefab, val2.m_category, Object.op_Implicit((Object)(object)val2.m_craftingStation) ? ((Object)val2.m_craftingStation).name : "none", val2.m_enabled, val2.m_dlc, flag, prefab.GetComponentsInChildren<StationExtension>(true).Length)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Add-on check " + def.Prefab + ": " + ex)); } } } private static void CreateUnlockItem(AddonDef d) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) string text = (Object.op_Implicit((Object)(object)PrefabManager.Instance.GetPrefab("SurtlingCore")) ? "SurtlingCore" : "Resin"); CustomItem val = new CustomItem("TP_Unlock_" + d.Id, text); SharedData shared = val.ItemDrop.m_itemData.m_shared; shared.m_name = "<color=#7FD4FF>Unlock: " + d.Display + "</color>"; shared.m_description = "Lets everyone in this world build the " + d.Display + " (Hammer, Misc tab). " + d.Desc; shared.m_maxStackSize = 1; ShopItems.Tint(val.ItemPrefab, d.Tint); ItemManager.Instance.AddItem(val); d.UnlockItem = "TP_Unlock_" + d.Id; } public static AddonDef ForPiece(Piece p) { if ((Object)(object)p == (Object)null) { return null; } string n = Utils.GetPrefabName(((Component)p).gameObject); return Defs.FirstOrDefault((AddonDef d) => d.Prefab == n); } public static void RefreshLabels() { if ((Object)(object)ZoneSystem.instance == (Object)null) { return; } foreach (AddonDef def in Defs) { if (!((Object)(object)def.PieceRef == (Object)null)) { bool flag = IsUnlocked(def.UnlockId); def.PieceRef.m_name = (flag ? def.BaseName : (def.BaseName + " <color=#FF6060>(See Trader to Gain Access)</color>")); def.PieceRef.m_description = (flag ? def.Desc : ("See Trader to Gain Access. Buy the unlock from Loki.\n\n" + def.Desc)); } } } } [HarmonyPatch(typeof(Player), "UpdatePlacementGhost")] internal static class Patch_AddonGhost { private static void Postfix(Player __instance) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance.m_placementGhost == (Object)null)) { AddonDef addonDef = Addons.ForPiece(__instance.m_placementGhost.GetComponent<Piece>()); if (addonDef != null && !Addons.IsUnlocked(addonDef.UnlockId)) { __instance.m_placementStatus = (PlacementStatus)1; __instance.SetPlacementGhostValid(false); } } } } [HarmonyPatch(typeof(Player), "TryPlacePiece")] internal static class Patch_AddonPlace { private static bool Prefix(Piece piece, ref bool __result) { AddonDef addonDef = Addons.ForPiece(piece); if (addonDef == null || Addons.IsUnlocked(addonDef.UnlockId)) { return true; } MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, "See Trader to Gain Access", 0, (Sprite)null, false, true); } __result = false; return false; } } public class AddonTag : MonoBehaviour { public AddonKind Kind; } public class AddonStation : MonoBehaviour, Hoverable, Interactable { public AddonKind Kind; private int _selected; private float _lastReq; public string GetHoverName() { return Kind.ToString(); } public float GetHoverOffset() { return 1f; } public string GetHoverText() { try { switch (Kind) { case AddonKind.Repair: return "Tinker's Bench\n[<color=yellow><b>E</b></color>] Repair everything"; case AddonKind.Vault: return Vault.HoverText(); case AddonKind.Hearth: return "Trickster's Hearth\nComfort " + (((Component)this).GetComponent<Piece>()?.m_comfort ?? 0); case AddonKind.Fortune: return "Seer's Cauldron\n[<color=yellow><b>E</b></color>] Offer a Fortune Token (you have " + Fortune.TokenCount(Player.m_localPlayer) + ")"; case AddonKind.Anvil: return "Brokk's Borrowed Anvil\n[<color=yellow><b>E</b></color>] Smelt one ore\n[<color=yellow><b>Shift+E</b></color>] Smelt all ore"; case AddonKind.BountyBoard: return BoardText(); case AddonKind.BountyChest: case AddonKind.Obliterator: break; } } catch (Exception ex) { return "Error: " + ex.Message; } return ""; } private string BoardText() { if (Time.time - _lastReq > 2f) { _lastReq = Time.time; BountyClient.RequestState(); } BountyState state = BountyClient.State; if (state == null) { return "Trickster's Errands\n<i>Reading the board...</i>"; } StringBuilder stringBuilder = new StringBuilder("<b>Trickster's Errands</b> (day " + state.Day + ")\n"); for (int i = 0; i < state.Bounties.Count; i++) { Bounty bounty = state.Bounties[i]; string text = string.Format("{0} {1} {2} {3}/{4} <color=yellow>{5} coins</color>", (bounty.Kind == "K") ? "Kill" : "Gather", bounty.Amount, BountyClient.NameOf(bounty), bounty.Progress, bounty.Amount, bounty.Reward); if (bounty.Done) { text = "<color=#80FF80>DONE</color> " + text; } stringBuilder.Append((i == _selected % Mathf.Max(1, state.Bounties.Count)) ? "<color=orange>> </color>" : " ").Append(text).Append('\n'); } stringBuilder.Append("[<color=yellow><b>E</b></color>] Next errand [<color=yellow><b>Shift+E</b></color>] Dismiss selected (" + ((state.DismissCost == 0) ? "free" : (state.DismissCost + " coins")) + ")\n"); stringBuilder.Append("<size=80%>Kills count automatically. Gather items go in the Errand Drop.</size>"); return stringBuilder.ToString(); } public bool Interact(Humanoid user, bool hold, bool alt) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) if (!hold) { Player val = (Player)(object)((user is Player) ? user : null); if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer)) { try { switch (Kind) { case AddonKind.Repair: AddonActions.Repair(val); return true; case AddonKind.Anvil: AddonActions.Smelt(val, alt); return true; case AddonKind.Vault: if (alt) { Vault.Withdraw(val); } else { Vault.Deposit(val); } return true; case AddonKind.Fortune: Fortune.Use(val, ((Component)this).transform.position); return true; case AddonKind.Hearth: return false; case AddonKind.BountyBoard: { BountyState state = BountyClient.State; if (state == null || state.Bounties.Count == 0) { BountyClient.RequestState(); return true; } if (!alt) { _selected = (_selected + 1) % state.Bounties.Count; return true; } BountyClient.Dismiss(val, _selected % state.Bounties.Count); return true; } case AddonKind.BountyChest: case AddonKind.Obliterator: break; } } catch (Exception ex) { Plugin.Log.LogError((object)("Add-on interact failed (" + Kind.ToString() + "): " + ex)); } return false; } } return false; } public bool UseItem(Humanoid user, ItemData item) { return false; } } public static class AddonActions { public static void Repair(Player p) { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) int num = 0; foreach (ItemData allItem in ((Humanoid)p).GetInventory().GetAllItems()) { if (allItem?.m_shared != null && allItem.m_shared.m_useDurability) { float maxDurability = allItem.GetMaxDurability(); if (allItem.m_durability < maxDurability) { allItem.m_durability = maxDurability; num++; } } } ((Character)p).Message((MessageType)2, (num > 0) ? string.Format("Repaired {0} item{1}", num, (num == 1) ? "" : "s") : "Nothing needs repairing", 0, (Sprite)null, false); if (num > 0) { Fx.Repair(((Component)p).transform.position); } Plugin.Log.LogInfo((object)("Tinker's Bench: repaired " + num + " items")); } public static void Smelt(Player p, bool all) { //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = ((Humanoid)p).GetInventory(); int num = 0; string arg = null; for (int i = 0; i < 10000; i++) { ItemData val = ((IEnumerable<ItemData>)inventory.GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData val2) => (Object)(object)val2?.m_dropPrefab != (Object)null && Addons.SmeltMap.ContainsKey(((Object)val2.m_dropPrefab).name))); if (val == null) { break; } string text = Addons.SmeltMap[((Object)val.m_dropPrefab).name]; GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text); if ((Object)(object)itemPrefab == (Object)null) { break; } inventory.RemoveItem(val, 1); if (inventory.AddItem(text, 1, 1, 0, 0L, "", false, false) == null) { Object.Instantiate<GameObject>(itemPrefab, ((Component)p).transform.position + ((Component)p).transform.forward + Vector3.up, Quaternion.identity); } num++; arg = Localization.instance.Localize(itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_name); if (!all) { break; } } if (num > 0) { Fx.Smelt(((Component)p).transform.position); } ((Character)p).Message((MessageType)2, (num > 0) ? $"Smelted {num} ore (last: {arg})" : "You have no ore to smelt", 0, (Sprite)null, false); Plugin.Log.LogInfo((object)("Anvil: smelted " + num)); } public static bool IsExcludedFromObliterator(ItemData it) { if (it == null || (Object)(object)it.m_dropPrefab == (Object)null) { return true; } string name = ((Object)it.m_dropPrefab).name; if (name == "Coins" || name == "Coal" || it.m_shared?.m_name == "$item_coins") { return true; } return (from x in Plugin.ObliteratorExclude.Value.Split(new char[1] { ',' }) select x.Trim()).Contains(name); } public static bool TryClaim(Container c, Player p) { if ((Object)(object)c.m_nview == (Object)null || !c.m_nview.IsValid()) { return false; } ZDO zDO = c.m_nview.GetZDO(); bool flag = zDO.HasOwner() && !zDO.IsOwner(); if (c.IsInUse() || (flag && OwnerIsPlayerNearby(zDO))) { ((Character)p).Message((MessageType)2, "Someone is using it.", 0, (Sprite)null, false); Plugin.Log.LogInfo((object)"Chest action refused: in use or owned by another player"); return false; } c.m_nview.ClaimOwnership(); try { c.Load(); } catch { } return true; } private static bool OwnerIsPlayerNearby(ZDO z) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer != (Object)(object)Player.m_localPlayer && Vector3.Distance(((Component)allPlayer).transform.position, z.GetPosition()) < 6f) { return true; } } return false; } public static void Obliterate(Container c, Player p) { if (ObliteratorNet.IsBusy(c)) { ((Character)p).Message((MessageType)2, "The gods are working...", 0, (Sprite)null, false); } else if (TryClaim(c, p) && CheckRoom(c, p)) { ObliteratorNet component = ((Component)c).GetComponent<ObliteratorNet>(); if ((Object)(object)component == (Object)null) { ConvertNow(c, p); } else { component.Begin(c, p); } } } private static bool CheckRoom(Container c, Player p) { Inventory inventory = c.GetInventory(); GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Coal"); if ((Object)(object)itemPrefab == (Object)null) { Plugin.Log.LogWarning((object)"Surtur's Pit: no Coal prefab"); return false; } int coalMax = itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_maxStackSize; List<ItemData> list = inventory.GetAllItems().ToList(); List<ItemData> list2 = list.Where((ItemData i) => !IsExcludedFromObliterator(i)).ToList(); int num = list2.Sum((ItemData i) => i.m_stack); if (num == 0) { ((Character)p).Message((MessageType)2, "Nothing to obliterate.", 0, (Sprite)null, false); return false; } int num2 = list.Where((ItemData i) => (Object)(object)i.m_dropPrefab != (Object)null && ((Object)i.m_dropPrefab).name == "Coal").Sum((ItemData i) => coalMax - i.m_stack); int num3 = inventory.GetWidth() * inventory.GetHeight() - (list.Count - list2.Count); if (num > num2 + num3 * coalMax) { ((Character)p).Message((MessageType)2, "There is no room for your blessing.", 0, (Sprite)null, false); return false; } return true; } public static void ConvertNow(Container c, Player p) { try { c.Load(); } catch { } Inventory inventory = c.GetInventory(); GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Coal"); if ((Object)(object)itemPrefab == (Object)null) { Plugin.Log.LogWarning((object)"Surtur's Pit: no Coal prefab"); return; } int coalMax = itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_maxStackSize; List<ItemData> list = inventory.GetAllItems().ToList(); List<ItemData> list2 = list.Where((ItemData i) => !IsExcludedFromObliterator(i)).ToList(); int num = list2.Sum((ItemData i) => i.m_stack); if (num == 0) { if (p != null) { ((Character)p).Message((MessageType)2, "Nothing to obliterate.", 0, (Sprite)null, false); } return; } int num2 = list.Count - list2.Count; int num3 = list.Where((ItemData i) => (Object)(object)i.m_dropPrefab != (Object)null && ((Object)i.m_dropPrefab).name == "Coal").Sum((ItemData i) => coalMax - i.m_stack); int num4 = inventory.GetWidth() * inventory.GetHeight() - num2; int num5 = num3 + num4 * coalMax; if (num > num5) { if (p != null) { ((Character)p).Message((MessageType)2, "There is no room for your blessing.", 0, (Sprite)null, false); } Plugin.Log.LogInfo((object)$"Surtur's Pit refused: {num} items, room {num5}"); return; } foreach (ItemData item in list2) { inventory.RemoveItem(item); } int num6 = num; while (num6 > 0) { int num7 = Mathf.Min(num6, coalMax); if (inventory.AddItem("Coal", num7, 1, 0, 0L, "", false, false) == null) { Plugin.Log.LogWarning((object)("Surtur's Pit: coal add failed, " + num6 + " lost")); break; } num6 -= num7; } c.Save(); if (p != null) { ((Character)p).Message((MessageType)2, $"Obliterated {num} items into {num} Coal", 0, (Sprite)null, false); } Plugin.Log.LogInfo((object)("Surtur's Pit: " + num + " items -> coal")); } public static void TurnIn(Container c, Player p) { BountyState state = BountyClient.State; if (state == null) { BountyClient.RequestState(); ((Character)p).Message((MessageType)2, "Look at the Trickster's Errands first, then try again.", 0, (Sprite)null, false); } else { if (!TryClaim(c, p)) { return; } HashSet<string> hashSet = new HashSet<string>(from b in state.Bounties where b.Kind == "G" && !b.Done select b.Target); Inventory inventory = c.GetInventory(); Dictionary<string, int> dictionary = new Dictionary<string, int>(); int num = 0; foreach (ItemData item in inventory.GetAllItems().ToList()) { num += item.m_stack; string text = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null); if (text != null && hashSet.Contains(text)) { dictionary[text] = (dictionary.TryGetValue(text, out var value) ? value : 0) + item.m_stack; } } if (num == 0) { ((Character)p).Message((MessageType)2, "The chest is empty.", 0, (Sprite)null, false); return; } inventory.RemoveAll(); c.Save(); Plugin.Log.LogInfo((object)("Bounty turn-in: destroyed " + num + ", counted " + string.Join(",", dictionary.Select((KeyValuePair<string, int> kv) => kv.Key + ":" + kv.Value)))); if (dictionary.Count == 0) { ((Character)p).Message((MessageType)2, "Nothing in there was wanted. It's gone now.", 0, (Sprite)null, false); } else { BountyClient.Give(p, dictionary); } } } } [HarmonyPatch(typeof(Container), "Interact")] internal static class Patch_ContainerInteract { private static bool Prefix(Container __instance, Humanoid character, bool hold, bool alt, ref bool __result) { AddonTag component = ((Component)__instance).GetComponent<AddonTag>(); if ((Object)(object)component != (Object)null && component.Kind == AddonKind.Obliterator && ObliteratorNet.IsBusy(__instance)) { if (character != null) { ((Character)character).Message((MessageType)2, "The gods are working...", 0, (Sprite)null, false); } __result = false; return false; } if ((Object)(object)component == (Object)null || !alt || hold) { return true; } Player val = (Player)(object)((character is Player) ? character : null); if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer) { return true; } try { if (component.Kind == AddonKind.Obliterator) { AddonActions.Obliterate(__instance, val); } else if (component.Kind == AddonKind.BountyChest) { AddonActions.TurnIn(__instance, val); } } catch (Exception ex) { Plugin.Log.LogError((object)("Chest action failed: " + ex)); } __result = true; return false; } } [HarmonyPatch(typeof(Container), "GetHoverText")] internal static class Patch_ContainerHover { private static void Postfix(Container __instance, ref string __result) { AddonTag component = ((Component)__instance).GetComponent<AddonTag>(); if (!((Object)(object)component == (Object)null)) { if (component.Kind == AddonKind.Obliterator) { __result += "\n[<color=yellow><b>Shift+E</b></color>] Obliterate contents into Coal"; } else if (component.Kind == AddonKind.BountyChest) { __result += "\n[<color=yellow><b>Shift+E</b></color>] Turn in for today's errands (destroys ALL contents)"; } } } } public static class UnlockNet { private const string Rpc = "TP_Unlock"; private const string RpcRes = "TP_UnlockRes"; private static ZRoutedRpc _on; public static void EnsureRpc() { if (ZRoutedRpc.instance != null && _on != ZRoutedRpc.instance) { _on = ZRoutedRpc.instance; _on.Register<string>("TP_Unlock", (Action<long, string>)OnReq); _on.Register<string>("TP_UnlockRes", (Action<long, string>)OnRes); } } public static void Request(string id, int paid) { EnsureRpc(); ShopNet.Pending = true; ShopNet.PendingSince = Time.time; ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "TP_Unlock", new object[1] { id + "|" + paid }); } private static void OnReq(long sender, string msg) { if (!ServerLogic.IsServer) { return; } int num = 0; string text; try { string[] p = msg.Split(new char[1] { '|' }); num = ((p.Length > 1 && int.TryParse(p[1], out var result)) ? Mathf.Max(0, result) : 0); AddonDef addonDef = Addons.Defs.FirstOrDefault((AddonDef x) => x.Id == p[0] && x.Price != null); int num2 = ((addonDef != null) ? Mathf.Min(num, addonDef.Price()) : 0); if (addonDef == null) { text = "fail|Unknown add-on.|0"; } else if (Addons.IsUnlocked(addonDef.UnlockId)) { text = $"fail|Already unlocked.|{num2}"; } else if (num != addonDef.Price()) { text = $"fail|That's not the price.|{num2}"; } else { ZoneSystem.instance.SetGlobalKey(Addons.Key(addonDef.UnlockId)); Addons.MarkUnlocked(addonDef.UnlockId); text = "ok|" + addonDef.Display + "|" + addonDef.UnlockId; Plugin.Log.LogInfo((object)("Unlocked add-on " + addonDef.Id + " for " + num)); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Unlock request: " + ex)); text = "fail|Error.|0"; } ZRoutedRpc.instance.InvokeRoutedRPC(sender, "TP_UnlockRes", new object[1] { text }); } private static void OnRes(long sender, string msg) { if (!ShopNet.FromServer(sender)) { Plugin.Log.LogWarning((object)"Ignored unlock reply not sent by the server"); return; } ShopNet.Pending = false; try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } string[] array = msg.Split(new char[1] { '|' }); if (array[0] == "ok") { if (array.Length > 2) { Addons.MarkUnlocked(array[2]); } ((Character)localPlayer).Message((MessageType)2, "Unlocked: " + ((array.Length > 1) ? array[1] : "add-on") + "! Find it in the Hammer's Misc tab.", 0, (Sprite)null, false); StoreGui instance = StoreGui.m_instance; if (instance != null) { Trader trader = instance.m_trader; if (trader != null) { ((Component)trader).GetComponent<TraderNPC>()?.SayBuy(); } } } else { if (array.Length > 2 && int.TryParse(array[2], out var result)) { ShopNet.GiveCoins(localPlayer, result); } ((Character)localPlayer).Message((MessageType)2, (array.Length > 1) ? array[1] : "Can't unlock that.", 0, (Sprite)null, false); } Plugin.Log.LogInfo((object)("Unlock result: " + msg)); Addons.RefreshAll(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Unlock result: " + ex)); } } } public sealed class Bounty { public string Kind; public string Target; public int Amount; public int Progress; public int Reward; public bool Done; public override string ToString() { return $"{Kind},{Target},{Amount},{Progress},{Reward},{(Done ? 1 : 0)}"; } public static Bounty Parse(string s) { string[] array = s.Split(new char[1] { ',' }); if (array.Length < 6) { return null; } return new Bounty { Kind = array[0], Target = array[1], Amount = int.Parse(array[2]), Progress = int.Parse(array[3]), Reward = int.Parse(array[4]), Done = (array[5] == "1") }; } } public sealed class BountyState { public int Day; public int Dismissals; public int DismissCost; public List<Bounty> Bounties = new List<Bounty>(); public string Serialize() { return Day + "|" + Dismissals + "|" + string.Join(";", Bounties.Select((Bounty b) => b.ToString())); } public static BountyState Parse(string s) { BountyState bountyState = new BountyState(); if (string.IsNullOrEmpty(s)) { return bountyState; } string[] array = s.Split(new char[1] { '|' }); bountyState.Day = int.Parse(array[0]); bountyState.Dismissals = int.Parse(array[1]); if (array.Length > 2 && array[2].Length > 0) { string[] array2 = array[2].Split(new char[1] { ';' }); foreach (string s2 in array2) { try { Bounty bounty = Bounty.Parse(s2); if (bounty != null) { bountyState.Bounties.Add(bounty); } } catch { } } } return bountyState; } } public static class BountyServer { private sealed class Group { public int Min; public int Max; public List<string> Kill = new List<string>(); public List<string> Gather = new List<string>(); } public const string HolderPrefab = "TP_DataHolder"; private static ZDO _holder; private static readonly Random Rnd = new Random(); private static List<Group> _groups; private static readonly string[][] KillNames = new string[4][] { new string[9] { "Boar", "Neck", "Deer", "Greyling", "Greydwarf", "Greydwarf_Shaman", "Greydwarf_Elite", "Skeleton", "Ghost" }, new string[10] { "Draugr", "Draugr_Ranged", "Blob", "Leech", "Wraith", "Wolf", "Hatchling", "Fenring", "Ulv", "StoneGolem" }, new string[9] { "Goblin", "GoblinArcher", "GoblinBrute", "Deathsquito", "Lox", "Seeker", "SeekerBrute", "Tick", "Gjall" }, new string[8] { "Charred_Melee", "Charred_Archer", "Charred_Mage", "Asksvin", "Volture", "Morgen", "FallenValkyrie", "BlobLava" } }; private static readonly string[][] GatherNames = new string[4][] { new string[16] { "Wood", "Stone", "Resin", "Flint", "Feathers", "LeatherScraps", "DeerHide", "Raspberry", "Blueberries", "Mushroom", "Thistle", "RoundLog", "FineWood", "BoneFragments", "GreydwarfEye", "TrollHide" }, new string[12] { "Guck", "Bloodbag", "Ooze", "WitheredBone", "Chain", "FreezeGland", "Obsidian", "WolfPelt", "WolfFang", "Crystal", "ElderBark", "Entrails" }, new string[12] { "Barley", "Flax", "LoxPelt", "Needle", "BlackMarble", "Sap", "Carapace", "YggdrasilWood", "ScaleHide", "Bilebag", "Wisp", "Softtissue" }, new string[8] { "CharredBone", "AskHide", "Grausten", "Blackwood", "SulfurStone", "CelestialFeather", "MoltenCore", "CharcoalResin" } }; private static readonly Dictionary<string, float> _rate = new Dictionary<string, float>(); private static readonly Dictionary<long, int> _killWindow = new Dictionary<long, int>(); private static readonly Dictionary<long, float> _killWindowStart = new Dictionary<long, float>(); private static void BuildGroups() { _groups = new List<Group>(); string[] array = Plugin.BountyPay.Value.Split(new char[1] { ',' }); HashSet<string> hashSet = new HashSet<string>(ShopItems.Gear.Select((Offer g) => g.Prefab).Concat(ShopItems.Ammo.Select((Offer a) => a.Prefab)).Concat(ShopItems.Foods.Select((Offer f) => f.Prefab)) .Concat(ShopItems.Premium.Select((Offer f) => f.Prefab)) .Concat(ShopItems.Meads.Select((Offer m) => m.Prefab)) .Concat(ShopItems.Fun.Select((Offer f) => f.Prefab))); for (int num = 0; num < 4; num++) { Group obj = new Group { Min = 150, Max = 300 }; try { string[] array2 = array[num].Split(new char[1] { '-' }); obj.Min = int.Parse(array2[0].Trim()); obj.Max = int.Parse(array2[1].Trim()); } catch { } string[] array3 = KillNames[num]; foreach (string text in array3) { ZNetScene instance = ZNetScene.instance; object obj3; if (instance == null) { obj3 = null; } else { GameObject prefab = instance.GetPrefab(text); obj3 = ((prefab != null) ? prefab.GetComponent<Character>() : null); } if ((Object)obj3 != (Object)null) { obj.Kill.Add(text); } else { Plugin.Log.LogWarning((object)("Bounty: creature missing, skipped " + text)); } } array3 = GatherNames[num]; foreach (string text2 in array3) { ObjectDB instance2 = ObjectDB.instance; if ((Object)(object)((instance2 != null) ? instance2.GetItemPrefab(text2) : null) != (Object)null && !hashSet.Contains(text2)) { obj.Gather.Add(text2); } else { Plugin.Log.LogWarning((object)("Bounty: item missing or sold in the shop, skipped " + text2)); } } _groups.Add(obj); } } public static long GetLong(string key) { return Holder().GetLong(key, 0L); } public static void SetLong(string key, long v) { ZDO obj = Holder(); Own(obj); obj.Set(key, v); } public static bool RateOk(long pid, string what, float seconds) { string key = what + pid; float realtimeSinceStartup = Time.realtimeSinceStartup; if (_rate.TryGetValue(key, out var value) && realtimeSinceStartup - value < seconds) { Plugin.Log.LogWarning((object)$"Rate limit: {what} from {pid}"); return false; } _rate[key] = realtimeSinceStartup; return true; } public static void ResetCache() { _holder = null; _groups = null; } public static long PidOf(long sender) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (sender == ZDOMan.GetSessionID()) { if (!((Object)(object)Player.m_localPlayer != (Object)null)) { return 0L; } return Player.m_localPlayer.GetPlayerID(); } ZNet instance = ZNet.instance; ZNetPeer val = ((instance != null) ? instance.GetPeer(sender) : null); if (val == null) { return 0L; } ZDO zDO = ZDOMan.instance.GetZDO(val.m_characterID); if (zDO == null) { return 0L; } return zDO.GetLong(ZDOVars.s_playerID, 0L); } public static long PeerOf(long pid) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer != (Object)null && Player.m_localPlayer.GetPlayerID() == pid) { return ZDOMan.GetSessionID(); } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { ZDO zDO = ZDOMan.instance.GetZDO(peer.m_characterID); if (zDO != null && zDO.GetLong(ZDOVars.s_playerID, 0L) == pid) { return peer.m_uid; } } return 0L; } public static bool KillAllowed(long pid) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (!_killWindowStart.TryGetValue(pid, out var value) || realtimeSinceStartup - value > 60f) { _killWindowStart[pid] = realtimeSinceStartup; _killWindow[pid] = 0; } _killWindow[pid]++; return _killWindow[pid] <= 100; } public static bool IsOpenGather(long pid, string target) { return Load(pid).Bounties.Any((Bounty b) => b.Kind == "G" && !b.Done && b.Target == target); } private static ZDO Holder() { //IL_0076: Unknown result type (might be due to invalid IL or missing references) if (_holder != null && _holder.IsValid() && _holder.GetPrefab() == StringExtensionMethods.GetStableHashCode("TP_DataHolder")) { return _holder; } _holder = null; List<ZDO> list = ServerLogic.FindAll("TP_DataHolder"); if (list.Count > 0) { _holder = list[0]; } else { int stableHashCode = StringExtensionMethods.GetStableHashCode("TP_DataHolder"); _holder = ZDOMan.instance.CreateNewZDO(new Vector3(10400f, -200f, 10400f), stableHashCode); _holder.Persistent = true; _holder.SetPrefab(stableHashCode); Plugin.Log.LogInfo((object)"Created Loki's Stall data holder"); } return _holder; } private static void Own(ZDO z) { if (!z.IsOwner()) { z.SetOwner(ZDOMan.GetSessionID()); } } private static int Today() { if (!((Object)(object)EnvMan.instance != (Object)null)) { return 0; } return EnvMan.instance.GetCurrentDay(); } public static int DismissCost(int n) { int result; List<int> list = (from x in Plugin.DismissCosts.Value.Split(new char[1] { ',' }) select int.TryParse(x.Trim(), out result) ? result : 0).ToList(); if (list.Count == 0) { return 0; } return list[Mathf.Min(n, list.Count - 1)]; } private static Bounty Roll(BountyState st) { if (_groups == null) { BuildGroups(); } int num = Mathf.Clamp((ShopServer.MaxTier() - 1) / 2, 0, 3); int num2 = ((Rnd.NextDouble() < 0.6) ? num : Rnd.Next(num + 1)); for (int i = 0; i < 50; i++) { Group obj = _groups[num2]; bool flag = Rnd.Next(2) == 0; List<string> list = (flag ? obj.Kill : obj.Gather); if (list.Count == 0) { list = (flag ? obj.Gather : obj.Kill); flag = !flag; } if (list.Count == 0) { num2 = Mathf.Max(0, num2 - 1); continue; } string t = list[Rnd.Next(list.Count)]; if (!st.Bounties.Any((Bounty b) => b.Target == t)) { int reward = Mathf.RoundToInt(Mathf.Lerp((float)obj.Min, (float)obj.Max, (float)Rnd.NextDouble()) / 10f) * 10; return new Bounty { Kind = (flag ? "K" : "G"), Target = t, Amount = (flag ? (5 + Rnd.Next(11)) : (10 + Rnd.Next(21))), Reward = reward }; } } return null; } public static BountyState Load(long pid) { BountyState bountyState = BountyState.Parse(Holder().GetString("b_" + pid, "")); int num = Today(); if (bountyState.Bounties.Count == 0 || bountyState.Day != num) { if (bountyState.Bounties.Count > 0) { Plugin.Log.LogInfo((object)$"Day changed for {pid} ({bountyState.Day} -> {num}); old bounties replaced: {bountyState.Serialize()}"); } bountyState = new BountyState { Day = num }; for (int i = 0; i < Plugin.BountiesPerDay.Value; i++) { Bounty bounty = Roll(bountyState); if (bounty != null) { bountyState.Bounties.Add(bounty); } } Save(pid, bountyState); Plugin.Log.LogInfo((object)$"New bounties for {pid} (day {num}): {bountyState.Serialize()}"); } bountyState.DismissCost = DismissCost(bountyState.Dismissals); return bountyState; } public static void Save(long pid, BountyState st) { ZDO obj = Holder(); Own(obj); obj.Set("b_" + pid, st.Serialize()); } public static void Reset(long pid) { ZDO obj = Holder(); Own(obj); obj.Set("b_" + pid, ""); } public static int Progress(long pid, string kind, string target, int amount, out string msg) { msg = null; BountyState bountyState = Load(pid); int num = 0; foreach (Bounty item in bountyState.Bounties.Where((Bounty x) => !x.Done && x.Kind == kind && x.Target == target)) { item.Progress = Mathf.Min(item.Amount, item.Progress + amount); if (item.Progress >= item.Amount) { item.Done = true; num += item.Reward; msg = $"Errand complete! +{item.Reward} coins"; } } Save(pid, bountyState); return num; } public static int MaxDismissCost() { int result; return (from x in Plugin.DismissCosts.Value.Split(new char[1] { ',' }) select int.TryParse(x.Trim(), out result) ? result : 0).DefaultIfEmpty(0).Max(); } public static string Dismiss(long pid, int idx, int paid, out int refund) { refund = Mathf.Min(paid, MaxDismissCost()); BountyState bountyState = Load(pid); if (idx < 0 || idx >= bountyState.Bounties.Count) { return "No such errand."; } if (bountyState.Bounties[idx].Done) { return "That one is already done."; } int num = DismissCost(bountyState.Dismissals); if (paid != num) { return "That's not the price."; } Bounty bounty = Roll(bountyState); if (bounty == null) { return "No other errands right now."; } bountyState.Bounties[idx] = bounty; bountyState.Dismissals++; Save(pid, bountyState); refund = 0; Plugin.Log.LogInfo((object)$"Bounty dismissed by {pid} for {num}: now {bounty}"); return null; } } public static class BountyClient { private const string RpcReq = "TP_BReq"; private const string RpcState = "TP_BState"; private const string RpcKill = "TP_BKill"; private const string RpcGive = "TP_BGive"; private const string RpcDismiss = "TP_BDismiss"; private const string RpcMsg = "TP_BMsg"; private static ZRoutedRpc _on; public static BountyState State; private static float _lastDismiss = -10f; public static void EnsureRpc() { if (ZRoutedRpc.instance != null && _on != ZRoutedRpc.instance) { _on = ZRoutedRpc.instance; _on.Register<string>("TP_BReq", (Action<long, string>)OnReq); _on.Register<string>("TP_BState", (Action<long, string>)OnState); _on.Register<string>("TP_BKill", (Action<long, string>)OnKill); _on.Register<string>("TP_BGive", (Action<long, string>)OnGive); _on.Register<string>("TP_BDismiss", (Action<long, string>)OnDismiss); _on.Register<string>("TP_BMsg", (Action<long, string>)OnMsg); } } private static long MyId() { if (!((Object)(object)Player.m_localPlayer != (Object)null)) { return 0L; } return Player.m_localPlayer.GetPlayerID(); } private static void ToServer(string rpc, string msg) { EnsureRpc(); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), rpc, new object[1] { msg }); } public static void RequestState() { if ((Object)(object)Player.m_localPlayer != (Object)null) { ToServer("TP_BReq", MyId().ToString()); } } public static string NameOf(Bounty b) { try { if (b.Kind == "K") { ZNetScene instance = ZNetScene.instance; object obj; if (instance == null) { obj = null; } else { GameObject prefab = instance.GetPrefab(b.Target); obj = ((prefab != null) ? prefab.GetComponent<Character>() : null); } Character val = (Character)obj; return ((Object)(object)val != (Object)null) ? Localization.instance.Localize(val.m_name) : b.Target; } ObjectDB instance2 = ObjectDB.instance; object obj2; if (instance2 == null) { obj2 = null; } else { GameObject itemPrefab = instance2.GetItemPrefab(b.Target); obj2 = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null); } ItemDrop val2 = (ItemDrop)obj2; return ((Object)(object)val2 != (Object)null) ? Localization.instance.Localize(val2.m_itemData.m_shared.m_name) : b.Target; } catch { return b.Target; } } public static void Give(Player p, Dictionary<string, int> counts) { ToServer("TP_BGive", MyId() + "|" + string.Join(",", counts.Select((KeyValuePair<string, int> kv) => kv.Key + ":" + kv.Value))); } public static void Dismiss(Player p, int idx) { BountyState state = State; if (state == null || Time.time - _lastDismiss < 1.5f) { return; } _lastDismiss = Time.time; int dismissCost = state.DismissCost; if (dismissCost > 0) { if (ShopClient.CountCoins(p) < dismissCost) { ((Character)p).Message((MessageType)2, "Not enough coins to dismiss.", 0, (Sprite)null, false); return; } ShopClient.RemoveCoins(p, dismissCost); } Plugin.Log.LogInfo((object)$"Bounty dismiss request: #{idx} cost {dismissCost}"); ToServer("TP_BDismiss", $"{MyId()}|{idx}|{dismissCost}"); State = null; } public static void ReportKill(Player killer, string prefab) { ToServer("TP_BKill", killer.GetPlayerID() + "|" + prefab); } private static bool FromServer(long sender) { if (ZRoutedRpc.instance != null) { return sender == ZRoutedRpc.instance.GetServerPeerID(); } return false; } private static void Reply(long sender, long pid) { BountyState bountyState = BountyServer.Load(pid); ZRoutedRpc.instance.InvokeRoutedRPC(sender, "TP_BState", new object[1] { pid + "#" + bountyState.Serialize() + "#" + bountyState.DismissCost }); } private static void Pay(long peer, long pid, int coins, string msg) { if (peer == 0L) { Plugin.Log.LogWarning((object)$"Bounty payout of {coins} for {pid} has no connected peer; not delivered"); return; } ZRoutedRpc.instance.InvokeRoutedRPC(peer, "TP_BMsg", new object[1] { $"{pid}|{coins}|{msg}" }); } private static void OnReq(long sender, string msg) { if (!ServerLogic.IsServer || !Addons.IsUnlocked("board")) { return; } long num = BountyServer.PidOf(sender); if (num == 0L) { return; } try { Reply(sender, num); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Bounty state: " + ex)); } } private static void OnKill(long sender, string msg) { if (!ServerLogic.IsServer || !Addons.IsUnlocked("board")) { return; } try { string[] array = msg.Split(new char[1] { '|' }); long num = long.Parse(array[0]); if (!BountyServer.KillAllowed(num)) { Plugin.Log.LogWarning((object)("Bounty: kill rate limit hit for " + num)); return; } string msg2; int num2 = BountyServer.Progress(num, "K", array[1], 1, out msg2); if (num2 > 0) { Pay(BountyServer.PeerOf(num), num, num2, msg2); Plugin.Log.LogInfo((object)$"Bounty paid {num2} to {num} (kill {array[1]})"); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Bounty kill: " + ex)); } } private static void OnGive(long sender, string msg) { if (!ServerLogic.IsServer) { return; } try { long num = BountyServer.PidOf(sender); if (num == 0L) { return; } if (!Addons.IsUnlocked("board")) { Pay(sender, num, 0, "The Trickster's Errands is locked."); return; } string[] array = msg.Split(new char[1] { '|' }); int num2 = 0; string text = null; string[] array2 = ((array.Length > 1) ? array[1] : "").Split(new char[1] { ',' }); for (int i = 0; i < array2.Length; i++) { string[] array3 = array2[i].Split(new char[1] { ':' }); if (array3.Length < 2 || !int.TryParse(array3[1], out var result) || result <= 0) { continue; } if (!BountyServer.IsOpenGather(num, array3[0])) { Plugin.Log.LogInfo((object)$"Bounty turn-in from {num}: {array3[0]} is not an open bounty, ignored"); continue; } num2 += BountyServer.Progress(num, "G", array3[0], Mathf.Min(result, 10000), out var msg2); if (msg2 != null) { text = msg2; } } Plugin.Log.LogInfo((object)string.Format("Bounty turn-in from {0}: {1} -> paid {2}", num, (array.Length > 1) ? array[1] : "", num2)); Pay(sender, num, num2, text ?? "Turned in. Progress saved."); Reply(sender, num); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Bounty give: " + ex)); } } private static void OnDismiss(long sender, string msg) { if (!ServerLogic.IsServer) { return; } long num = 0L; int num2 = 0; try { num = BountyServer.PidOf(sender); if (num != 0L) { string[] array = msg.Split(new char[1] { '|' }); num2 = Mathf.Min(Mathf.Max(0, int.Parse(array[2])), BountyServer.MaxDismissCost()); if (!Addons.IsUnlocked("board")) { Pay(sender, num, num2, "The Trickster's Errands is locked. Refunded."); return; } int refund; string text = BountyServer.Dismiss(num, int.Parse(array[1]), num2, out refund); Pay(sender, num, refund, text ?? "Errand replaced."); Reply(sender, num); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Bounty dismiss: " + ex)); Pay(sender, num, num2, "Error, refunded."); } } private static void OnState(long sender, string msg) { if (!FromServer(sender)) { return; } try { string[] array = msg.Split(new char[1] { '#' }); if (long.Parse(array[0]) == MyId()) { State = BountyState.Parse(array[1]); State.DismissCost = int.Parse(array[2]); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Bounty state parse: " + ex.Message)); } } private static void OnMsg(long sender, string msg) { if (!FromServer(sender)) { Plugin.Log.LogWarning((object)"Ignored bounty payout not sent by the server"); return; } try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } string[] array = msg.Split(new char[1] { '|' }, 3); if (array.Length >= 3 && long.Parse(array[0]) == localPlayer.GetPlayerID()) { int num = int.Parse(array[1]); if (num > 0) { ShopNet.GiveCoins(localPlayer, num); } ((Character)localPlayer).Message((MessageType)2, array[2] + ((num > 0 && !array[2].Contains("+")) ? $" (+{num} coins)" : ""), 0, (Sprite)null, false); Plugin.Log.LogInfo((object)$"Bounty message: {array[2]} ({num} coins)"); RequestState(); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Bounty message: " + ex)); } } } [HarmonyPatch(typeof(Character), "OnDeath")] internal static class Patch_KillCredit { private static int _warned; private static void Prefix(Character __instance) { try { if (!((Object)(object)__instance == (Object)null) && !__instance.IsPlayer() && Addons.IsUnlocked("board") && !__instance.IsTamed()) { HitData lastHit = __instance.m_lastHit; Character obj = ((lastHit != null) ? lastHit.GetAttacker() : null); Player val = (Player)(object)((obj is Player) ? obj : null); if ((Object)(object)val == (Object)null) { Plugin.Log.LogDebug((object)("Kill credit: no Player attacker for " + ((Object)__instance).name)); } else { BountyClient.ReportKill(val, Utils.GetPrefabName(((Component)__instance).gameObject)); } } } catch (Exception ex) { if (_warned++ < 5) { Plugin.Log.LogWarning((object)("Kill credit: " + ex.Message)); } } } } internal static class Commands { private class Cmd : ConsoleCommand { private readonly string _name; private readonly string _help; private readonly string _action; public override string Name => _name; public override string Help => _help; public override bool IsCheat => true; public Cmd(string n, string h, string a) { _name = n; _help = h; _action = a; } public override void Run(string[] args) { if ((Object)(object)ZNet.instance == (Object)null) { Console.instance.Print("Load a world first."); return; } string text = ((_action == "newbounties" && (Object)(object)Player.m_localPlayer != (Object)null) ? ("newbounties:" + Player.m_localPlayer.GetPlayerID()) : _action); if (ZNet.instance.IsServer()) { Console.instance.Print("[TradingPost] " + Execute(text)); if (text.StartsWith("newbounties")) { BountyClient.RequestState(); } } else { EnsureRpc(); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "TP_DebugCmd", new object[1] { text }); } } } public const string RpcName = "TP_DebugCmd"; private static ZRoutedRpc _registeredOn; public static void Register() { CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new Cmd("tp_spawntrader", "Loki's Stall: force the Trader to appear at the post now", "spawn")); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new Cmd("tp_reset", "Loki's Stall: remove the Trader and reset the sleep counter", "reset")); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new Cmd("tp_status", "Loki's Stall: show post / sleep / trader state", "status")); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new Cmd("tp_unlockall", "Loki's Stall: unlock every add-on in this world", "unlockall")); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new Cmd("tp_lockall", "Loki's Stall: lock every add-on again", "lockall")); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new Cmd("tp_newbounties", "Loki's Stall: give yourself a fresh set of errands", "newbounties")); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new Cmd("tp_rotate", "Loki's Stall: roll a new shop stock now and restart the timer", "rotate")); } internal static string Execute(string action) { switch (action) { case "spawn": return ServerLogic.ForceSpawn(); case "reset": return ServerLogic.ResetState(); case "unlockall": foreach (AddonDef def in Addons.Defs) { ZoneSystem.instance.SetGlobalKey(Addons.Key(def.UnlockId)); Addons.MarkUnlocked(def.UnlockId); } Addons.RefreshAll(); return "All add-ons unlocked."; case "lockall": foreach (AddonDef def2 in Addons.Defs) { ZoneSystem.instance.RemoveGlobalKey(Addons.Key(def2.UnlockId)); } Addons.ClearLocal(); Addons.RefreshAll(); return "All add-ons locked."; case "rotate": { if (!ServerLogic.IsServer) { return "Not the server."; } ZDO primaryPost = ServerLogic.GetPrimaryPost(); if (primaryPost == null) { return "No Loki's Stall in this world."; } ShopServer.ForceRotate(primaryPost); return "Shop restocked. Max gear tier: " + ShopServer.MaxTier(); } default: { if (action.StartsWith("newbounties:") && long.TryParse(action.Substring(12), out var result)) { BountyServer.Reset(result); BountyState bountyState = BountyServer.Load(result); return "New errands: " + bountyState.Serialize(); } return ServerLogic.Status(); } } } internal static void EnsureRpc() { if (ZRoutedRpc.instance == null || _registeredOn == ZRoutedRpc.instance) { return; } _registeredOn = ZRoutedRpc.instance; ZRoutedRpc.instance.Register<string>("TP_DebugCmd", (Action<long, string>)delegate(long sender, string action) { if (!((Object)(object)ZNet.instance == (Object)null)) { if (ZNet.instance.IsServer()) { ZNetPeer peer = ZNet.instance.GetPeer(sender); string text = ((sender == ZDOMan.GetSessionID() || (peer != null && ZNet.instance.IsAdmin(peer.m_socket.GetHostName()))) ? Execute(action) : "You must be an admin."); ZRoutedRpc.instance.InvokeRoutedRPC(sender, "TP_DebugCmd", new object[1] { "msg:" + text }); } else if (action.StartsWith("msg:")) { Console instance = Console.instance; if (instance != null) { instance.Print("[TradingPost] " + action.Substring(4)); } } } }); } } public class SE_TPResting : StatusEffect { public override void Setup(Character character) { ((StatusEffect)this).Setup(character); try { StatusEffect obj = character.GetSEMan().AddStatusEffect(SEMan.s_statusEffectRested, true, 0, 0f, (short)(-1)); SE_Rested val = (SE_Rested)(object)((obj is SE_Rested) ? obj : null); if (val != null) { val.m_baseTTL = (float)Plugin.RestingMinutes.Value * 60f; val.m_TTLPerComfortLevel = 0f; ((StatusEffect)val).m_ttl = val.m_baseTTL; ((StatusEffect)val).m_time = 0f; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Resting potion: " + ex.Message)); } } } public class SE_TPFalling : SE_Stats { public override void Setup(Character character) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: 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_0085: 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_009f: 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) ((SE_Stats)this).Setup(character); if ((Object)(object)character != (Object)(object)Player.m_localPlayer) { return; } if (IsCovered((Player)character)) { character.Message((MessageType)2, "You need open sky above you. The potion is returned.", 0, (Sprite)null, false); ((Humanoid)(Player)character).GetInventory().AddItem("TP_PotionFalling", 1, 1, 0, 0L, "", false, false); ((StatusEffect)this).m_ttl = 0.1f; return; } Vector3 position = ((Component)character).transform.position + Vector3.up * 61f; ((Component)character).transform.position = position; if ((Object)(object)character.m_body != (Object)null) { character.m_body.position = position; character.m_body.velocity = Vector3.zero; } } public static bool IsCovered(Player p) { //IL_0072: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) if (((Character)p).InInterior() || ((Character)p).InWater() || ((Character)p).IsSwimming() || ((Character)p).IsAttachedToShip() || (Object)(object)((Character)p).GetStandingOnShip() != (Object)null) { return true; } int mask = LayerMask.GetMask(new string[6] { "Default", "static_solid", "piece", "terrain", "Default_small", "vehicle" }); RaycastHit val = default(RaycastHit); return Physics.SphereCast(((Component)p).transform.position + Vector3.up * 1f, 0.5f, Vector3.up, ref val, 65f, mask, (QueryTriggerInteraction)1); } } public class SE_TPBeacon : StatusEffect { public override void Setup(Character character) { ((StatusEffect)this).Setup(character); if ((Object)(object)character == (Object)(object)Player.m_localPlayer) { ShopNet.RequestBeacon(); } } } [HarmonyPatch(typeof(Player), "CanConsumeItem")] internal static class Patch_CanConsume { private static void Postfix(Player __instance, ItemData item, ref bool __result) { if (__result && !((Object)(object)item?.m_dropPrefab == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && ((Object)item.m_dropPrefab).name == "TP_PotionFalling" && SE_TPFalling.IsCovered(__instance)) { ((Character)__instance).Message((MessageType)2, "You need open sky above you (not indoors, on a ship or in water).", 0, (Sprite)null, false); __result = false; } } } [HarmonyPatch(typeof(Player), "UpdateFood")] internal static class Patch_UpdateFood { private static void Postfix(Player __instance) { foreach (Food food in __instance.m_foods) { GameObject val = food?.m_item?.m_dropPrefab; if (!((Object)(object)val == (Object)null) && ((Object)val).name.StartsWith("TP_Food")) { SharedData shared = food.m_item.m_shared; food.m_health = shared.m_food; food.m_stamina = shared.m_foodStamina; food.m_eitr = shared.m_foodEitr; } } } } public static class Fx { private static EffectList _repair; private static EffectList _coins; private static EffectList _bossStart; private static EffectList _lever; private static GameObject _lightning; private static float _oblDelayMin = 2f; private static float _oblDelayMax = 3f; private static bool _triedRepair; private static bool _triedCoins; private static bool _triedLever; private static float _triedBossAt = -999f; private static EffectList _smelt; private static EffectList _summon; private static bool _triedSmelt; private static bool _triedSummon; private static GameObject Prefab(string name) { if (!((Object)(object)ZNetScene.instance != (Object)null)) { return null; } return ZNetScene.instance.GetPrefab(name); } private static void Play(EffectList fx, Vector3 pos, string what) { //IL_000c: 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_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) try { if (fx != null && fx.HasEffects()) { fx.Create(pos, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Effect " + what + " failed: " + ex.Message)); } } public static void Repair(Vector3 pos) { //IL_00a4: Unknown result type (might be due to invalid IL or missing references) if (!_triedRepair) { _triedRepair = true; string[] array = new string[2] { "piece_workbench", "forge" }; foreach (string text in array) { GameObject obj = Prefab(text); CraftingStation val = ((obj != null) ? obj.GetComponent<CraftingStation>() : null); if (val?.m_repairItemDoneEffects != null && val.m_repairItemDoneEffects.HasEffects()) { _repair = val.m_repairItemDoneEffects; Plugin.Log.LogInfo((object)("Fx: repair effect from " + text)); break; } } if (_repair == null) { Plugin.Log.LogWarning((object)"Fx: no repair effect found (piece_workbench/forge m_repairItemDoneEffects)"); } } Play(_repair, pos, "repair"); } public static void Smelt(Vector3 pos) { //IL_0099: Unknown result type (might be due to invalid IL or missing references) if (!_triedSmelt) { _triedSmelt = true; GameObject obj = Prefab("smelter"); Smelter val = ((obj != null) ? obj.GetComponent<Smelter>() : null); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)"Fx: vanilla 'smelter' prefab not found; anvil is silent"); } else { _smelt = ((val.m_produceEffects != null && val.m_produceEffects.HasEffects()) ? val.m_produceEffects : val.m_oreAddedEffects); } if ((Object)(object)val != (Object)null && (_smelt == null || !_smelt.HasEffects())) { Plugin.Log.LogWarning((object)"Fx: smelter has no produce/ore-added effects"); } } Play(_smelt, pos, "smelt"); } private static bool SoundOnly(GameObject g) { if ((Object)(object)g != (Object)null && ((Object)(object)g.GetComponentInChildren<AudioSource>(true) != (Object)null || (Object)(object)g.GetComponentInChildren<ZSFX>(true) != (Object)null) && (Object)(object)g.GetComponentInChildren<ParticleSystem>(true) == (Object)null && (Object)(object)g.GetComponentInChildren<Renderer>(true) == (Object)null) { return (Object)(object)g.GetComponentInChildren<Light>(true) == (Object)null; } return false; } public static void SummonSound(Vector3 pos) { //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Expected O, but got Unknown //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Expected O, but got Unknown //IL_01da: 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_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Expected O, but got Unknown if (!_triedSummon) { _triedSummon = true; try { ObjectDB instance = ObjectDB.instance; object obj; if (instance == null) { obj = null; } else { GameObject itemPrefab = instance.GetItemPrefab("StaffSkeleton"); obj = ((itemPrefab == null) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_attack); } Attack val = (Attack)obj; if (val == null) { Plugin.Log.LogWarning((object)"Fx: StaffSkeleton not found"); } else { EffectData[] array = (from e in ((IEnumerable<EffectList>)(object)new EffectList[3] { val.m_startEffect, val.m_triggerEffect, val.m_burstEffect }).Where((EffectList l) => l?.m_effectPrefabs != null).SelectMany((EffectList l) => l.m_effectPrefabs) where e != null && e.m_enabled && SoundOnly(e.m_prefab) select e).ToArray(); if (array.Length != 0) { _summon = new EffectList { m_effectPrefabs = array }; Plugin.Log.LogInfo((object)("Fx: summon sound " + string.Join(",", array.Select((EffectData e) => ((Object)e.m_prefab).name)))); } } if (_summon == null) { GameObject val2 = new string[3] { "sfx_staff_summon", "sfx_dead_raiser_summon", "sfx_summon_skeleton" }.Select(Prefab).FirstOrDefault((Func<GameObject, bool>)SoundOnly); if ((Object)(object)val2 != (Object)null) { EffectList val3 = new EffectList(); val3.m_effectPrefabs = (EffectData[])(object)new EffectData[1] { new EffectData { m_prefab = val2, m_enabled = true } }; _summon = val3; } Plugin.Log.LogWarning((object)("Fx: no sound-only StaffSkeleton effect; fallback " + (((Object)(object)val2 != (Object)null) ? ((Object)val2).name : "none"))); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Fx: summon sound: " + ex.Message)); } } Play(_summon, pos, "summon sound"); } public static void Coins(Vector3 pos, bool deposit) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) if (!_triedCoins) { _triedCoins = true; StoreGui val = StoreGui.m_instance ?? Object.FindObjectOfType<StoreGui>(true); if ((Object)(object)val != (Object)null) { _coins = ((val.m_sellEffects != null && val.m_sellEffects.HasEffects()) ? val.m_sellEffects : val.m_buyEffects); } if (_coins == null || !_coins.HasEffects()) { Plugin.Log.LogWarning((object)"Fx: no StoreGui buy/sell coin effect found"); } else { Plugin.Log.LogInfo((object)"Fx: coin sound from StoreGui"); } } Play(_coins, pos, "coins"); } public static void BossSummon(Vector3 pos) { //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Expected O, but got Unknown //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Expected O, but got Unknown //IL_018c: Unknown result type (might be due to invalid IL or missing references) if (_bossStart == null && Time.time - _triedBossAt > 30f) { _triedBossAt = Time.time; try { OfferingBowl[] array = Resources.FindObjectsOfTypeAll<OfferingBowl>(); foreach (OfferingBowl val in array) { if (!((Object)(object)val == (Object)null)) { if (val.m_spawnBossStartEffects != null && val.m_spawnBossStartEffects.HasEffects()) { _bossStart = val.m_spawnBossStartEffects; Plugin.Log.LogInfo((object)("Fx: boss summon effect from " + ((Object)val).name)); break; } if (_bossStart == null && val.m_fuelAddedEffects != null && val.m_fuelAddedEffects.HasEffects()) { _bossStart = val.m_fuelAddedEffects; } } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Fx: OfferingBowl scan: " + ex.Message)); } if (_bossStart == null) { GameObject val2 = new string[3] { "fx_GP_Activation", "vfx_offering", "sfx_offering" }.Select(Prefab).FirstOrDefault((Func<GameObject, bool>)((GameObject g) => (Object)(object)g != (Object)null)); if ((Object)(object)val2 != (Object)null) { Plugin.Log.LogWarning((object)("Fx: no OfferingBowl loaded, using fallback " + ((Object)val2).name)); EffectList val3 = new EffectList(); val3.m_effectPrefabs = (EffectData[])(object)new EffectData[1] { new EffectData { m_prefab = val2, m_enabled = true } }; Play(val3, pos, "boss summon fallback"); return; } Plugin.Log.LogWarning((object)"Fx: no boss summon effect or fallback found"); } } Play(_bossStart, pos, "boss summon"); } private static void LoadLever() { if (_triedLever) { return; } _triedLever = true; GameObject obj = Prefab("incinerator"); Incinerator val = ((obj != null) ? obj.GetComponent<Incinerator>() : null); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)"Fx: vanilla 'incinerator' prefab not found; Surtur's Pit runs without effects"); return; } _lever = val.m_leverEffects; _lightning = val.m_lightingAOEs; if (val.m_effectDelayMax > 0f) { _oblDelayMin = val.m_effectDelayMin; _oblDelayMax = val.m_effectDelayMax; } Plugin.Log.LogInfo((object)$"Fx: incinerator lever={_lever != null && _lever.HasEffects()} lightning={(Object)(object)_lightning != (Object)null} delay {_oblDelayMin}-{_oblDelayMax}s"); } private static bool IsVisual(Component c) { if (!(c is Transform) && !(c is ParticleSystem) && !(c is Renderer) && !(c is MeshFilter) && !(c is Light) && !(c is AudioSource) && !(c is ZSFX) && !(c is TimedDestruction) && !(c is LightFlicker) && !(c is LightLod)) { return c is Animator; } return true; } public static float ObliterateDelay() { LoadLever(); return Mathf.Clamp(Random.Range(_oblDelayMin, _oblDelayMax), 1f, 10f); } public static void Lever(Vector3 pos) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) LoadLever(); Play(_lever, pos, "incinerator lever"); } public static void Lightning(Transform at) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) LoadLever(); if ((Object)(object)_lightning == (Object)null) { return; } try { bool activeSelf = _lightning.activeSelf; _lightning.SetActive(false); GameObject val = Object.Instantiate<GameObject>(_lightning, at.position, at.rotation); _lightning.SetActive(activeSelf); for (int i = 0; i < 10; i++) { int num = 0; Component[] componentsInChildren = val.GetComponentsInChildren<Component>(true); foreach (Component val2 in componentsInChildren) { if (!((Object)(object)val2 == (Object)null) && !IsVisual(val2)) { Object.DestroyImmediate((Object)(object)val2); num++; } } if (num == 0) { break; } } if (val.GetComponentsInChildren<ZNetView>(true).Length != 0 || val.GetComponentsInChildren<Aoe>(true).Length != 0 || val.GetComponentsInChildren<Collider>(true).Length != 0 || val.GetComponentsInChildren<Rigidbody>(true).Length != 0) { Object.DestroyImmediate((Object)(object)val); Plugin.Log.LogWarning((object)"Fx: lightning clone still had gameplay components; skipped (visual not shown)"); } else { val.SetActive(true); Object.Destroy((Object)(object)val, 6f); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Fx: lightning: " + ex.Message)); } } } public class ObliteratorNet : MonoBehaviour { public const string KeyUntil = "tp_obl_until_t"; private float _lastFx = -99f; private bool _registered; private ZNetView _nv; private void Awake() { _nv = ((Component)this).GetComponent<ZNetView>(); TryRegister(); } private void Start() { if ((Object)(object)_nv == (Object)null) { _nv = ((Component)this).GetComponent<ZNetView>(); } TryRegister(); } private void TryRegister() { if (!_registered && !((Object)(object)_nv == (Object)null) && _nv.IsValid()) { _nv.Register("TP_OblFx", (Action<long>)RPC_Fx); _registered = true; } } public static bool IsBusy(Container c) { object obj; if (c == null) { obj = null; } else { ZNetView nview = c.m_nview; obj = ((nview != null) ? nview.GetZDO() : null); } ZDO val = (ZDO)obj; if (val != null && (Object)(object)ZNet.instance != (Object)null) { return val.GetLong("tp_obl_until_t", 0L) > ZNet.instance.GetTime().Ticks; } return false; } private void RPC_Fx(long sender) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_nv == (Object)null) && _nv.IsValid() && sender == _nv.GetZDO().GetOwner() && !(Time.time - _lastFx < 1f)) { _lastFx = Time.time; Fx.Lever(((Component)this).transform.position); Fx.Lightning(((Component)this).transform); } } public void Begin(Container c, Player p) { float num = Fx.ObliterateDelay(); _nv.GetZDO().Set("tp_obl_until_t", ZNet.instance.GetTime().Ticks + TimeSpan.FromSeconds(num + 1f).Ticks); try { _nv.InvokeRPC(ZNetView.Everybody, "TP_OblFx", Array.Empty<object>()); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Surtur's Pit fx rpc: " + ex.Message)); } ((Character)p).Message((MessageType)2, "The gods are working...", 0, (Sprite)null, false); Plugin.Log.LogInfo((object)$"Surtur's Pit: locked for {num:0.0}s"); ((MonoBehaviour)this).StartCoroutine(Finish(c, p, num)); } private IEnumerator Finish(Container c, Player p, float delay) { yield return (object)new WaitForSeconds(delay); try { if ((Object)(object)_nv == (Object)null || !_nv.IsValid()) { yield break; } if (!_nv.IsOwner()) { Plugin.Log.LogWarning((object)"Surtur's Pit: lost ownership during the effect; nothing converted"); yield break; } InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance != (Object)null && instance.IsContainerOpen() && (Object)(object)instance.m_currentContainer == (Object)(object)c) { instance.CloseContainer(); } AddonActions.ConvertNow(c, Object.op_Implicit((Object)(object)p) ? p : Player.m_localPlayer); } catch (Exception ex) { Plugin.Log.LogError((object)("Surtur's Pit convert: " + ex)); } finally { if ((Object)(object)_nv != (Object)null && _nv.IsValid() && _nv.IsOwner()) { _nv.GetZDO().Set("tp_obl_until_t", 0L); } } } } public class BoardLabel : MonoBehaviour { private void Start() { Apply(((Component)this).gameObject); } public static void Apply(GameObject root) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) try { TMP_Text[] componentsInChildren = root.GetComponentsInChildren<TMP_Text>(true); if (componentsInChildren.Length == 0) { Plugin.Log.LogWarning((object)"Trickster's Errands: no sign text widget found"); return; } TMP_Text[] array = componentsInChildren; foreach (TMP_Text obj in array) { obj.text = "Errands"; ((Graphic)obj).color = Color.white; ((Component)obj).gameObject.SetActive(true); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Trickster's Errands label: " + ex.Message)); } } } public static class GuideNames { public const string Title = "Loki's Trading Post"; public const string Post = "Loki's Stall"; public const string Board = "Trickster's Errands"; public const string Chest = "Errand Drop"; public const string Repair = "Tinker's Bench"; public const string Anvil = "Brokk's Borrowed Anvil"; public const string Obliterator = "Surtur's Pit"; public const string Vault = "Vault"; public const string Hearth = "Trickster's Hearth"; public const string Bowl = "Seer's Cauldron"; public const string Token = "Fortune Token"; } public static class Guide { private static string H(string s) { return "\n<b><color=orange>" + s + "</color></b>\n"; } private static string N(int v) { return v.ToString("N0"); } private static string Costs() { List<string> list = (from x in Plugin.DismissCosts.Value.Split(new char[1] { ',' }) select x.Trim() into x where x.Length > 0 select x).ToList(); if (list.Count == 0) { return "free"; } string text = ((list[0] == "0") ? "first is free each day" : ("first costs " + list[0] + " gold")); List<string> list2 = (from x in list.Skip(1) select (!(x == "0")) ? x : "free").ToList(); if (list2.Count != 0) { return text + ", then " + string.Join(", ", list2) + " gold"; } return text; } public static string Text() { string value = Plugin.TraderName.Value; string text = ((Plugin.VaultWithdraw.Value > 0) ? (N(Plugin.VaultWithdraw.Value) + " coins") : "one stack"); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("A wandering trader named " + value + " sells strong gear, food, potions and add-ons, all for gold.\n"); stringBuilder.Append(H("Build the post")); stringBuilder.Append(string.Format("Hammer > Misc > {0}. Needs a workbench nearby. Cost: {1} Fine Wood, {2} Stone, {3} Deer Hide, {4} Bronze Nails. Only one per world.\n", "Loki's Stall", Plugin.CostFineWood.Value, Plugin.CostStone.Value, Plugin.CostDeerHide.Value, Plugin.CostBronzeNails.Value)); stringBuilder.Append(H(value + " arrives")); stringBuilder.Append($"Sleep near it. Each night he may show up; by sleep {Plugin.GuaranteedSleep.Value} he always does. If the post is destroyed, {value} leaves; build it again and he returns the same way.\n"); stringBuilder.Append(H("Don't hit him")); stringBuilder.Append(value + " cannot be hurt. Keep hitting him and he will hit back. Hard.\n"); stringBuilder.Append(H("The shop")); stringBuilder.Append($"Press E on {value}. 12 items; the stock changes every {Plugin.RotationMinutes.Value} minutes of play (timer in the shop window). Shift-click Buy to buy 5 (arrows come in bundles of {Plugin.AmmoBundle.Value}). A <color=#FFD700>gold name</color> is a rare super item: stronger, more bonuses, double price. Defeat bosses to unlock better gear.\n"); stringBuilder.Append(H("Selling")); stringBuilder.Append(value + " also buys gems, amber and pearls.\n"); stringBuilder.Append(H("Add-ons")); stringBuilder.Append("Locked add-ons are in Hammer > Misc; buy the unlock from " + value + ". Unlocks are for everyone and survive a rebuild.\n"); stringBuilder.Append(H("Trickster's Errands (" + N(Plugin.PriceBoard.Value) + " gold)")); stringBuilder.Append(string.Format("You get {0} errands a day: kill or gather. E on the board picks an errand, Shift+E swaps it for a new one ({1}). Kills count automatically. For gather errands, put the items in the {2} and press Shift+E. <color=#FF6060>Careful: the chest destroys EVERYTHING inside, even items no errand wants.</color>\n", Plugin.BountiesPerDay.Value, Costs(), "Errand Drop")); stringBuilder.Append(H("Other add-ons")); stringBuilder.Append("<b>Tinker's Bench</b> (" + N(Plugin.PriceRepair.Value) + "): E repairs all your gear. No shelter needed.\n"); stringBuilder.Append("<b>Brokk's Borrowed Anvil</b> (" + N(Plugin.PriceAnvil.Value) + "): E smelts one ore into a bar, Shift+E smelts all. No fuel.\n"); stringBuilder.Append("<b>Surtur's Pit</b> (" + N(Plugin.PriceObliterator.Value) + "): Shift+E turns everything inside into Coal, 1 for 1. Coins, boss items and key items are left alone. Needs room for the coal.\n"); stringBuilder.Append("<b>Vault</b> (" + N(Plugin.PriceVault.Value) + "): E deposits all your coins, Shift+E takes out " + text + ". Your balance is your own and the same at every vault. Up to " + N(Plugin.VaultDailyCap.Value) + " a day.\n"); stringBuilder.Append(string.Format("<b>{0}</b> ({1}): {2} comfort, +{3} for each add-on within 20 m (max {4}). Gives no heat; you still need a fire.\n", "Trickster's Hearth", N(Plugin.PriceHearth.Value), Plugin.HearthBase.Value, Plugin.HearthPerStation.Value, Plugin.HearthMax.Value)); stringBuilder.Append("<b>Seer's Cauldron</b> (" + N(Plugin.PriceFortune.Value) + "): E uses a Fortune Token (" + N(Plugin.PriceToken.Value) + " gold from " + value + ") to reveal one item from the next stock. " + ((Plugin.FortunePerDay.Value == 1) ? "One use" : (Plugin.FortunePerDay.Value + " uses")) + " per day.\n"); return stringBuilder.ToString(); } } [HarmonyPatch(typeof(TextsDialog), "UpdateTextsList")] internal static class Patch_GuideEntry { private static void Postfix(TextsDialog __instance) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown try { if (__instance.m_texts != null) { __instance.m_texts.RemoveAll((TextInfo t) => t != null && t.m_topic == "Loki's Trading Post"); __instance.m_texts.Insert(0, new TextInfo("Loki's Trading Post", Guide.Text())); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Guide entry: " + ex.Message)); } } } internal static class PostCheck { public static bool IsOurPiece(Piece piece) { if ((Object)(object)piece != (Object)null) { return Utils.GetPrefabName(((Component)piece).gameObject) == "TP_TradingPost"; } return false; } public static bool PostExists() { if ((Object)(object)ZoneSystem.instance != (Object)null) { return ZoneSystem.instance.GetGlobalKey("tradingpost_built"); } return false; } } [HarmonyPatch(typeof(EnvMan), "SkipToMorning")] internal static class Patch_SkipToMorning { private static void Postfix() { ServerLogic.OnSleep(); } } [HarmonyPatch(typeof(Player), "UpdatePlacementGhost")] internal static class Patch_UpdatePlacementGhost { private static void Postfix(Player __instance) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance.m_placementGhost == (Object)null) && PostCheck.IsOurPiece(__instance.m_placementGhost.GetComponent<Piece>()) && PostCheck.PostExists()) { __instance.m_placementStatus = (PlacementStatus)1; __instance.SetPlacementGhostValid(false); } } } [HarmonyPatch(typeof(Player), "TryPlacePiece")] internal static class Patch_TryPlacePiece { private static bool Prefix(Piece piece, ref bool __result) { if (!PostCheck.IsOurPiece(piece) || !PostCheck.PostExists()) { return true; } MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, "Only one Loki's Stall can exist in this world.", 0, (Sprite)null, false, true); } __result = false; return false; } } [HarmonyPatch(typeof(Trader), "Start")] internal static class Patch_TraderStart { private static bool Prefix(Trader __instance) { return (Object)(object)((Component)__instance).GetComponent<TraderNPC>() == (Object)null; } } [HarmonyPatch(typeof(Trader), "Update")] internal static class Patch_TraderUpdate { private static bool Prefix(Trader __instance) { return (Object)(object)((Component)__instance).GetComponent<TraderNPC>() == (Object)null; } } [HarmonyPatch(typeof(Trader), "Interact")] internal static class Patch_TraderInteract { private static bool Prefix(Trader __instance, Humanoid character, bool hold, bool alt, ref bool __result) { TraderNPC component = ((Component)__instance).GetComponent<TraderNPC>(); if ((Object)(object)component == (Object)null) { return true; } __result = component.Interact(character, hold, alt); return false; } } [HarmonyPatch(typeof(Trader), "GetHoverText")] internal static class Patch_TraderHover { private static bool Prefix(Trader __instance, ref string __result) { TraderNPC component = ((Component)__instance).GetComponent<TraderNPC>(); if ((Object)(object)component == (Object)null) { return true; } __result = component.GetHoverText(); return false; } } [HarmonyPatch(typeof(Trader), "GetHoverName")] internal static class Patch_TraderHoverName { private static bool Prefix(Trader __instance, ref string __result) { TraderNPC component = ((Component)__instance).GetComponent<TraderNPC>(); if ((Object)(object)component == (Object)null) { return true; } __result = component.GetHoverName(); return false; } } [HarmonyPatch(typeof(Trader), "OnSold")] internal static class Patch_TraderOnSold { private static bool Prefix(Trader __instance) { TraderNPC component = ((Component)__instance).GetComponent<TraderNPC>(); if ((Object)(object)component == (Object)null) { return true; } component.Say(__instance.m_randomSell[Random.Range(0, __instance.m_randomSell.Count)]); return false; } } [HarmonyPatch(typeof(Trader), "OnBought")] internal static class Patch_TraderOnBought { private static bool Prefix(Trader __instance) { TraderNPC component = ((Component)__instance).GetComponent<TraderNPC>(); if ((Object)(object)component == (Object)null) { return true; } component.Say(__instance.m_randomBuy[Random.Range(0, __instance.m_randomBuy.Count)]); return false; } } [HarmonyPatch(typeof(Aoe), "OnHit")] internal static class Patch_AoeOnHit { private static void Prefix() { TraderNPC.AoeDepth++; } private static void Finalizer() { TraderNPC.AoeDepth--; } } [HarmonyPatch(typeof(Attack), "DoAreaAttack")] internal static class Patch_AreaAttack { private static void Prefix() { TraderNPC.AoeDepth++; } private static void Finalizer() { TraderNPC.AoeDepth--; } } [HarmonyPatch(typeof(Game), "Start")] internal static class Patch_GameStart { private static void Postfix() { Commands.EnsureRpc(); ShopNet.EnsureRpc(); UnlockNet.EnsureRpc(); BountyClient.EnsureRpc(); BountyServer.ResetCache(); Addons.ClearLocal(); Vault.EnsureRpc(); Fortune.EnsureRpc(); Vault.ApplyCoinStack(); } } [HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[] { typeof(ItemData), typeof(int), typeof(bool), typeof(float), typeof(int), typeof(bool) })] internal static class Patch_TooltipSafety { private static readonly HashSet<string> Logged = new HashSet<string>(); private static Exception Finalizer(Exception __exception, ItemData item, ref string __result) { if (__exception == null) { return null; } string text = (((Object)(object)item?.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : item?.m_shared?.m_name); if (text == null || (!text.StartsWith("TP_") && !(item.m_shared?.m_name ?? "").StartsWith("Loki"))) { return __exception; } if (Logged.Add(text)) { Plugin.Log.LogWarning((object)("Tooltip failed for " + text + ", using a simple one: " + __exception)); } __result = Localization.instance.Localize(item.m_shared.m_description ?? ""); return null; } } [BepInPlugin("dbsteiger.TradingPost", "Loki's Trading Post", "0.5.6")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] [SynchronizationMode(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string ModGuid = "dbsteiger.TradingPost"; public const string ModName = "Loki's Trading Post"; public const string ModVersion = "0.5.6"; public const string PostPrefabName = "TP_TradingPost"; public const string TraderPrefabName = "TP_Trader"; public const string PostExistsKey = "tradingpost_built"; public const string ZdoSleeps = "tp_sleeps"; public const string ZdoTraderSpawned = "tp_trader_spawned"; public const string ZdoPrimary = "tp_primary"; public static Plugin Instance; public static ManualLogSource Log; public static ConfigEntry<int> ChanceSleep1; public static ConfigEntry<int> ChanceSleep2; public static ConfigEntry<int> ChanceSleep3; public static ConfigEntry<int> GuaranteedSleep; public static ConfigEntry<int> CostFineWood; public static ConfigEntry<int> CostStone; public static ConfigEntry<int> CostDeerHide; public static ConfigEntry<int> CostBronzeNails; public static ConfigEntry<string> TraderName; public static ConfigEntry<string> TierPrices; public static ConfigEntry<int> RotationMinutes; public static ConfigEntry<int> SuperChancePct; public static ConfigEntry<int> GearBonusPct; public static ConfigEntry<int> SuperBonusPct; public static ConfigEntry<int> ElementPct; public static ConfigEntry<int> HeavySpeedPct; public static ConfigEntry<int> BonusPricePct; public static ConfigEntry<int> AmmoBundle; public static ConfigEntry<int> AmmoPriceMin; public static ConfigEntry<int> AmmoPriceMax; public static ConfigEntry<int> FoodBonusPct; public static ConfigEntry<int> FoodMinutes; public static ConfigEntry<int> FoodPrice; public static ConfigEntry<int> PremiumFoodPrice; public static ConfigEntry<int> MeadPrice; public static ConfigEntry<int> FunPrice; public static ConfigEntry<int> RestingPrice; public static ConfigEntry<int> RestingMinutes; public static ConfigEntry<int> BeaconPrice; public static ConfigEntry<bool> GateByBoss; public static ConfigEntry<int> PriceBoard; public static ConfigEntry<int> PriceRepair; public static ConfigEntry<int> PriceAnvil; public static ConfigEntry<int> PriceObliterator; public static ConfigEntry<int> BountiesPerDay; public static ConfigEntry<int> PriceVault; public static ConfigEntry<int> PriceHearth; public static ConfigEntry<int> PriceFortune; public static ConfigEntry<int> PriceToken; public static ConfigEntry<int> CoinStack; public static ConfigEntry<int> VaultWithdraw; public static ConfigEntry<int> HearthBase; public static ConfigEntry<int> HearthPerStation; public static ConfigEntry<int> HearthMax; public static ConfigEntry<int> VaultDailyCap; public static ConfigEntry<int> FortunePerDay; public static ConfigEntry<st