Decompiled source of LokisTradingPost v0.5.6

plugins/TradingPost.dll

Decompiled 16 hours ago
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