Decompiled source of ThorsKist v0.8.6

plugins/ThorsKist.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("posixone")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+8fa289af510fd71e7f51dd6727e5ddfc9b53b31d")]
[assembly: AssemblyProduct("ThorsKist")]
[assembly: AssemblyTitle("ThorsKist")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace ThorsKist;

public static class ItemWire
{
	private const int InventoryVersion = 109;

	public static void WriteItems(ZPackage pkg, List<ItemData> items)
	{
		pkg.Write(109);
		pkg.Write((ushort)items.Count);
		for (int i = 0; i < items.Count; i++)
		{
			items[i].Save(pkg);
		}
	}

	public static void WriteItem(ZPackage pkg, ItemData item)
	{
		pkg.Write(109);
		pkg.Write((ushort)1);
		item.Save(pkg);
	}

	public static List<ItemData> ReadItems(ZPackage pkg)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		Inventory val = new Inventory("tk_scratch", (Sprite)null, 8, 256);
		val.Load(pkg);
		return new List<ItemData>(val.GetAllItems());
	}
}
public enum ItemCategory
{
	Weapon,
	Ammo,
	Armor,
	Tool,
	Food,
	Seed,
	Wood,
	Stone,
	Ore,
	Hide,
	Parts,
	Fibre,
	Gem,
	Trophy,
	Fish,
	Valuable,
	Other
}
public static class KistCategories
{
	public static readonly string[] Names = new string[17]
	{
		"Weapon", "Ammo", "Armour", "Tool", "Food", "Seeds", "Wood", "Stone", "Ore & Metal", "Hides",
		"Parts", "Fibre", "Gems & Cores", "Trophy", "Fish", "Valuables", "Other"
	};

	public static readonly string[] ExpectedItemTypes = new string[23]
	{
		"Material=1", "Consumable=2", "OneHandedWeapon=3", "Bow=4", "Shield=5", "Helmet=6", "Chest=7", "Ammo=9", "Customization=10", "Legs=11",
		"Hands=12", "Trophy=13", "TwoHandedWeapon=14", "Torch=15", "Misc=16", "Shoulder=17", "Utility=18", "Tool=19", "Attach_Atgeir=20", "Fish=21",
		"TwoHandedWeaponLeft=22", "AmmoNonEquipable=23", "Trinket=24"
	};

	public static string NameOf(ItemCategory c)
	{
		if (c < ItemCategory.Weapon || (int)c >= Names.Length)
		{
			return "";
		}
		return Names[(int)c];
	}

	public static bool FromItemType(int itemType, out ItemCategory category)
	{
		switch (itemType)
		{
		case 3:
		case 4:
		case 14:
		case 20:
		case 22:
			category = ItemCategory.Weapon;
			return true;
		case 9:
		case 23:
			category = ItemCategory.Ammo;
			return true;
		case 5:
		case 6:
		case 7:
		case 10:
		case 11:
		case 12:
		case 17:
		case 18:
		case 24:
			category = ItemCategory.Armor;
			return true;
		case 15:
		case 19:
			category = ItemCategory.Tool;
			return true;
		case 2:
			category = ItemCategory.Food;
			return true;
		case 13:
			category = ItemCategory.Trophy;
			return true;
		case 21:
			category = ItemCategory.Fish;
			return true;
		default:
			category = ItemCategory.Other;
			return false;
		}
	}

	public static ItemCategory FromToken(string token)
	{
		if (string.IsNullOrEmpty(token))
		{
			return ItemCategory.Other;
		}
		string haystack = token.ToLowerInvariant();
		if (Has(haystack, "seed", "acorn", "pinecone", "sapling", "cone"))
		{
			return ItemCategory.Seed;
		}
		if (Has(haystack, "wood", "log", "bark", "branch", "stake", "beech", "birch"))
		{
			return ItemCategory.Wood;
		}
		if (Has(haystack, "hide", "pelt", "leather", "scale", "carapace", "chitin", "fur"))
		{
			return ItemCategory.Hide;
		}
		if (Has(haystack, "flax", "linen", "jute", "thread", "cloth", "yarn", "string"))
		{
			return ItemCategory.Fibre;
		}
		if (Has(haystack, "berry", "berries", "mushroom", "honey", "meat", "carrot", "turnip", "onion", "barley", "flour", "bread", "mead", "potion", "dandelion", "thistle", "sausage", "stew", "soup", "pie", "jam", "cream", "egg", "milk", "fish"))
		{
			return ItemCategory.Food;
		}
		if (Has(haystack, "bone", "fang", "tusk", "claw", "tooth", "horn", "eye", "gland", "sinew", "tissue", "mandible", "feather", "jelly", "entrail", "bloodbag", "ooze", "guck", "stinger", "wing", "bilebag", "tail", "sack", "heart", "brain", "neck", "trophy"))
		{
			return ItemCategory.Parts;
		}
		if (Has(haystack, "core", "crystal", "ruby", "amber", "gem", "pearl", "eitr", "sap", "tear", "wisp", "essence"))
		{
			return ItemCategory.Gem;
		}
		if (Has(haystack, "coin", "treasure", "necklace", "ring"))
		{
			return ItemCategory.Valuable;
		}
		if (Has(haystack, "stone", "flint", "marble", "obsidian", "grausten", "rock", "clay", "salt", "sulfur"))
		{
			return ItemCategory.Stone;
		}
		if (Has(haystack, "ore", "metal", "ingot", "bronze", "copper", "tin", "iron", "silver", "flametal", "nail", "chain", "scrap", "steel", "coal"))
		{
			return ItemCategory.Ore;
		}
		return ItemCategory.Other;
	}

	private static bool Has(string haystack, params string[] needles)
	{
		for (int i = 0; i < needles.Length; i++)
		{
			if (haystack.IndexOf(needles[i], StringComparison.Ordinal) >= 0)
			{
				return true;
			}
		}
		return false;
	}
}
public static class KistClient
{
	public class RosterEntry
	{
		public long PlayerId;

		public string Name = "";
	}

	private class PendingDeposit
	{
		public int Token;

		public List<ItemData> Items;

		public float Deadline;
	}

	public static Hird Hird;

	public static Inventory Kist;

	public static int MinHirdSize = 2;

	public static bool HasPendingInvite;

	public static string PendingInviteHird = "";

	public static string PendingInviteFrom = "";

	public static readonly List<RosterEntry> Roster = new List<RosterEntry>();

	public static bool AnchorAllowed;

	public static string AnchorReason = "";

	public static string AnchorCostPrefab = "";

	public static int AnchorCostAmount;

	public static Action OnStateChanged;

	private static readonly List<PendingDeposit> Pending = new List<PendingDeposit>();

	private static int _nextToken = 1;

	private static float _nextHello;

	private static bool _helloSent;

	private static float _nextPing;

	private static float _nextAnchorQuery;

	public static bool InHird => Hird != null;

	public static bool KistUnlocked
	{
		get
		{
			if (Hird != null)
			{
				return Hird.Members.Count >= MinHirdSize;
			}
			return false;
		}
	}

	public static bool IsOwner
	{
		get
		{
			Player localPlayer = Player.m_localPlayer;
			if (Hird != null && (Object)(object)localPlayer != (Object)null)
			{
				return Hird.OwnerId == localPlayer.GetPlayerID();
			}
			return false;
		}
	}

	public static float DistanceToAnchor
	{
		get
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (Hird == null || (Object)(object)localPlayer == (Object)null)
			{
				return float.MaxValue;
			}
			return Vector3.Distance(((Component)localPlayer).transform.position, Hird.Anchor);
		}
	}

	public static bool CanDeposit
	{
		get
		{
			if (KistUnlocked)
			{
				return InRange(Main.DepositRadius.Value);
			}
			return false;
		}
	}

	public static bool CanTake
	{
		get
		{
			if (KistUnlocked)
			{
				return InRange(Main.TakeRadius.Value);
			}
			return false;
		}
	}

	public static bool CanCraft
	{
		get
		{
			if (Main.CraftFromKist.Value && KistUnlocked)
			{
				return InRange(Main.CraftRadius.Value);
			}
			return false;
		}
	}

	public static bool InRange(float radius)
	{
		if (Main.UnlimitedRange.Value)
		{
			return true;
		}
		return DistanceToAnchor <= radius;
	}

	public static void Reset()
	{
		Hird = null;
		Kist = null;
		Roster.Clear();
		Pending.Clear();
		HasPendingInvite = false;
		AnchorAllowed = false;
		AnchorReason = "";
		_helloSent = false;
		_nextHello = 0f;
	}

	private static ZPackage NewMessage(Wire.C2S op)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Expected O, but got Unknown
		Player localPlayer = Player.m_localPlayer;
		ZPackage val = new ZPackage();
		val.Write(4);
		val.Write((byte)op);
		val.Write(((Object)(object)localPlayer != (Object)null) ? localPlayer.GetPlayerID() : 0);
		val.Write(((Object)(object)localPlayer != (Object)null) ? localPlayer.GetPlayerName() : "");
		val.Write(((Object)(object)localPlayer != (Object)null) ? ((Component)localPlayer).transform.position : Vector3.zero);
		return val;
	}

	private static void Send(ZPackage pkg)
	{
		if (ZRoutedRpc.instance != null)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC("ThorsKist_C2S", new object[1] { pkg });
		}
	}

	public static void RequestState()
	{
		Send(NewMessage(Wire.C2S.RequestState));
	}

	public static void CreateHird(string name)
	{
		ZPackage obj = NewMessage(Wire.C2S.CreateHird);
		obj.Write(name ?? "");
		Send(obj);
	}

	public static void Invite(long playerId, string name)
	{
		ZPackage obj = NewMessage(Wire.C2S.Invite);
		obj.Write(playerId);
		obj.Write(name ?? "");
		Send(obj);
	}

	public static void AcceptInvite()
	{
		Send(NewMessage(Wire.C2S.AcceptInvite));
	}

	public static void DeclineInvite()
	{
		Send(NewMessage(Wire.C2S.DeclineInvite));
	}

	public static void Leave()
	{
		Send(NewMessage(Wire.C2S.Leave));
	}

	public static void Disband()
	{
		Send(NewMessage(Wire.C2S.Disband));
	}

	public static void SetAnchorHere()
	{
		Send(NewMessage(Wire.C2S.SetAnchor));
	}

	public static void QueryAnchor()
	{
		Send(NewMessage(Wire.C2S.AnchorQuery));
	}

	public static void RenameHird(string name)
	{
		ZPackage obj = NewMessage(Wire.C2S.Rename);
		obj.Write(name ?? "");
		Send(obj);
	}

	public static int AnchorCostHeld()
	{
		if (string.IsNullOrEmpty(AnchorCostPrefab) || AnchorCostAmount <= 0)
		{
			return int.MaxValue;
		}
		string text = SharedNameOf(AnchorCostPrefab);
		Player localPlayer = Player.m_localPlayer;
		if (text == null || (Object)(object)localPlayer == (Object)null)
		{
			return 0;
		}
		return ((Humanoid)localPlayer).GetInventory().CountItems(text, -1, true);
	}

	public static string AnchorCostLabel()
	{
		if (string.IsNullOrEmpty(AnchorCostPrefab) || AnchorCostAmount <= 0)
		{
			return "";
		}
		string text = SharedNameOf(AnchorCostPrefab);
		string text2 = ((text != null && Localization.instance != null) ? Localization.instance.Localize(text) : AnchorCostPrefab);
		return AnchorCostAmount + " " + text2;
	}

	private static string SharedNameOf(string prefabName)
	{
		if ((Object)(object)ObjectDB.instance == (Object)null)
		{
			return null;
		}
		GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefabName);
		if ((Object)(object)itemPrefab == (Object)null)
		{
			return null;
		}
		ItemDrop component = itemPrefab.GetComponent<ItemDrop>();
		if (!((Object)(object)component != (Object)null))
		{
			return null;
		}
		return component.m_itemData.m_shared.m_name;
	}

	public static void Kick(long playerId)
	{
		ZPackage obj = NewMessage(Wire.C2S.Kick);
		obj.Write(playerId);
		Send(obj);
	}

	public static bool Deposit(Inventory from, ItemData item, int amount)
	{
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Expected O, but got Unknown
		if (item == null || from == null)
		{
			return false;
		}
		if (amount <= 0 || amount > item.m_stack)
		{
			amount = item.m_stack;
		}
		ItemData val = item.Clone();
		val.m_stack = amount;
		from.RemoveItem(item, amount);
		int num = _nextToken++;
		Pending.Add(new PendingDeposit
		{
			Token = num,
			Items = new List<ItemData> { val },
			Deadline = Time.realtimeSinceStartup + 15f
		});
		ZPackage obj = NewMessage(Wire.C2S.Deposit);
		obj.Write(num);
		ZPackage val2 = new ZPackage();
		ItemWire.WriteItems(val2, new List<ItemData> { val });
		obj.Write(val2);
		Send(obj);
		return true;
	}

	public static void Take(Vector2i slot, string expectName, int amount)
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		ZPackage obj = NewMessage(Wire.C2S.Take);
		obj.Write(_nextToken++);
		obj.Write(slot);
		obj.Write(expectName ?? "");
		obj.Write(amount);
		Send(obj);
	}

	public static void ReportDiag(string text)
	{
		if (!string.IsNullOrEmpty(text))
		{
			Main.Log.LogInfo((object)("ThorsKist: " + text));
			ZPackage obj = NewMessage(Wire.C2S.Diag);
			obj.Write(text);
			Send(obj);
		}
	}

	public static void ReportCraftConsume(List<CraftDraw> draws)
	{
		if (draws == null || draws.Count == 0)
		{
			return;
		}
		ZPackage val = NewMessage(Wire.C2S.ConsumeForCraft);
		val.Write(draws.Count);
		foreach (CraftDraw draw in draws)
		{
			val.Write(draw.Name);
			val.Write(draw.Amount);
			val.Write(draw.Quality);
		}
		Send(val);
	}

	public static void OnMessage(long sender, ZPackage pkg)
	{
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (pkg.ReadInt() != 4)
			{
				Message("Thor's Kist: version mismatch with the server.");
				return;
			}
			switch ((Wire.S2C)pkg.ReadByte())
			{
			case Wire.S2C.State:
				ReadState(pkg);
				break;
			case Wire.S2C.Toast:
				Message(pkg.ReadString());
				break;
			case Wire.S2C.Denied:
				Message(pkg.ReadString());
				break;
			case Wire.S2C.DepositAck:
				ClearPending(pkg.ReadInt());
				break;
			case Wire.S2C.DepositRejected:
				RollBack(pkg.ReadInt(), pkg.ReadString());
				break;
			case Wire.S2C.AnchorSet:
				OnAnchorSet(pkg.ReadVector3(), pkg.ReadLong());
				break;
			case Wire.S2C.DepositFx:
				KistFx.DepositAt(pkg.ReadVector3());
				break;
			case Wire.S2C.AnchorStatus:
			{
				bool num = pkg.ReadBool();
				string text = pkg.ReadString();
				string text2 = pkg.ReadString();
				int num2 = pkg.ReadInt();
				bool flag = num != AnchorAllowed || text != AnchorReason || text2 != AnchorCostPrefab || num2 != AnchorCostAmount;
				AnchorAllowed = num;
				AnchorReason = text;
				AnchorCostPrefab = text2;
				AnchorCostAmount = num2;
				if (flag)
				{
					KistUI.MarkDirty();
				}
				break;
			}
			case Wire.S2C.TakeResult:
				ReceiveTake(pkg.ReadInt(), pkg.ReadPackage());
				break;
			}
		}
		catch (Exception ex)
		{
			Main.Log.LogError((object)("ThorsKist: bad reply from the server: " + ex));
		}
	}

	private static void ReadState(ZPackage pkg)
	{
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Expected O, but got Unknown
		if (pkg.ReadBool())
		{
			Hird = Hird.ReadHeader(pkg);
			MinHirdSize = pkg.ReadInt();
			ZPackage val = pkg.ReadPackage();
			if (Kist == null)
			{
				Kist = new Inventory("Kist", (Sprite)null, 8, 256);
			}
			Kist.Load(val);
			HasPendingInvite = false;
		}
		else
		{
			Hird = null;
			Kist = null;
			HasPendingInvite = pkg.ReadBool();
			if (HasPendingInvite)
			{
				PendingInviteHird = pkg.ReadString();
				PendingInviteFrom = pkg.ReadString();
			}
		}
		Roster.Clear();
		int num = pkg.ReadInt();
		for (int i = 0; i < num; i++)
		{
			RosterEntry rosterEntry = new RosterEntry();
			rosterEntry.PlayerId = pkg.ReadLong();
			rosterEntry.Name = pkg.ReadString();
			Roster.Add(rosterEntry);
		}
		if (OnStateChanged != null)
		{
			OnStateChanged();
		}
	}

	private static void OnAnchorSet(Vector3 pos, long payerPlayerId)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		KistFx.Anchor(pos);
		Player localPlayer = Player.m_localPlayer;
		if (!((Object)(object)localPlayer == (Object)null) && payerPlayerId != 0L && payerPlayerId == localPlayer.GetPlayerID() && !string.IsNullOrEmpty(AnchorCostPrefab) && AnchorCostAmount > 0)
		{
			string text = SharedNameOf(AnchorCostPrefab);
			if (text != null)
			{
				((Humanoid)localPlayer).GetInventory().RemoveItem(text, AnchorCostAmount, -1, true);
				Message("The Kist took " + AnchorCostLabel() + ".");
			}
		}
	}

	private static void ClearPending(int token)
	{
		Pending.RemoveAll((PendingDeposit p) => p.Token == token);
	}

	private static void RollBack(int token, string why)
	{
		PendingDeposit pendingDeposit = Pending.Find((PendingDeposit x) => x.Token == token);
		Pending.RemoveAll((PendingDeposit x) => x.Token == token);
		if (pendingDeposit != null)
		{
			GiveBack(pendingDeposit.Items);
		}
		if (!string.IsNullOrEmpty(why))
		{
			Message(why);
		}
	}

	private static void ReceiveTake(int token, ZPackage payload)
	{
		List<ItemData> list = ItemWire.ReadItems(payload);
		if (list.Count != 0)
		{
			GiveBack(list);
			KistFx.Take();
		}
	}

	private static void GiveBack(List<ItemData> items)
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return;
		}
		Inventory inventory = ((Humanoid)localPlayer).GetInventory();
		foreach (ItemData item in items)
		{
			if (inventory.CanAddItem(item, item.m_stack))
			{
				inventory.AddItem(item);
				continue;
			}
			DropAtFeet(localPlayer, item);
			Message("$msg_yourinventoryisfull");
		}
	}

	private static void DropAtFeet(Player p, ItemData item)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		Vector3 val = ((Component)p).transform.position + ((Component)p).transform.forward * 1.5f + Vector3.up;
		ItemDrop.DropItem(item, item.m_stack, val, Quaternion.identity);
	}

	public static void Message(string text)
	{
		if (!string.IsNullOrEmpty(text))
		{
			if ((Object)(object)MessageHud.instance != (Object)null)
			{
				MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true);
			}
			else
			{
				Main.Log.LogInfo((object)("ThorsKist: " + text));
			}
		}
	}

	public static void Tick()
	{
		KistServer.Tick();
		if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
		{
			if (_helloSent)
			{
				Reset();
			}
			return;
		}
		if (!_helloSent && Time.realtimeSinceStartup >= _nextHello)
		{
			_nextHello = Time.realtimeSinceStartup + 2f;
			if (ZRoutedRpc.instance != null && Player.m_localPlayer.GetPlayerID() != 0L)
			{
				Send(NewMessage(Wire.C2S.Hello));
				_helloSent = true;
			}
		}
		if (InHird && Time.realtimeSinceStartup >= _nextPing)
		{
			_nextPing = Time.realtimeSinceStartup + 3f;
			Send(NewMessage(Wire.C2S.Ping));
		}
		if (InHird && KistUI.WantsAnchorStatus && Time.realtimeSinceStartup >= _nextAnchorQuery)
		{
			_nextAnchorQuery = Time.realtimeSinceStartup + 1.5f;
			QueryAnchor();
		}
		for (int num = Pending.Count - 1; num >= 0; num--)
		{
			if (!(Time.realtimeSinceStartup < Pending[num].Deadline))
			{
				List<ItemData> items = Pending[num].Items;
				Pending.RemoveAt(num);
				GiveBack(items);
				Message("The Kist did not answer. Your items were returned.");
			}
		}
	}
}
public class CraftDraw
{
	public string Name;

	public int Amount;

	public int Quality = -1;
}
public static class KistConflicts
{
	public enum Stake
	{
		Display,
		Deposit,
		Payment
	}

	public sealed class Conflict
	{
		public string Mod;

		public string Method;

		public bool Replaces;

		public Stake Stake;

		public string Key => Mod + "|" + Method;
	}

	private static readonly List<Conflict> _found = new List<Conflict>();

	private static readonly HashSet<string> _seen = new HashSet<string>();

	private static int _scans;

	private static bool _sawPlayer;

	private static bool _told;

	public static bool AnyWorthTelling
	{
		get
		{
			foreach (Conflict item in _found)
			{
				if (item.Stake != Stake.Display)
				{
					return true;
				}
			}
			return false;
		}
	}

	public static IList<Conflict> All => _found;

	public static void Reset()
	{
		_told = false;
	}

	public static void Tick()
	{
		if (_scans == 0)
		{
			_scans++;
			Scan();
			return;
		}
		if (!_sawPlayer && (Object)(object)Player.m_localPlayer != (Object)null)
		{
			_sawPlayer = true;
			Scan();
		}
		if (!_told && AnyWorthTelling && !((Object)(object)Player.m_localPlayer == (Object)null))
		{
			_told = true;
			KistDialog.Show("THOR'S KIST - SHARED HOOKS", Report());
		}
	}

	private static IEnumerable<MethodBase> Seams()
	{
		Harmony harmonyInstance = Main.HarmonyInstance;
		if (harmonyInstance == null)
		{
			yield break;
		}
		IEnumerable<MethodBase> enumerable;
		try
		{
			enumerable = harmonyInstance.GetPatchedMethods();
		}
		catch (Exception ex)
		{
			Main.Log.LogWarning((object)("ThorsKist: could not list our own patches: " + ex.Message));
			enumerable = new MethodBase[0];
		}
		foreach (MethodBase item in enumerable)
		{
			if (!(item == null) && !(item.DeclaringType == null))
			{
				Type declaringType = item.DeclaringType;
				if (declaringType == typeof(Inventory) || declaringType == typeof(Player) || declaringType == typeof(InventoryGui))
				{
					yield return item;
				}
			}
		}
	}

	private static Stake StakeOf(MethodBase m)
	{
		switch (m.Name)
		{
		case "CountItems":
		case "RemoveItem":
		case "ConsumeResources":
		case "HaveItem":
		case "GetItem":
			return Stake.Payment;
		case "DoCrafting":
			return Stake.Display;
		case "OnSelectedItem":
			return Stake.Deposit;
		default:
			return Stake.Display;
		}
	}

	private static void Scan()
	{
		int num = 0;
		foreach (MethodBase item in Seams())
		{
			num++;
			string text = ((item.DeclaringType != null) ? (item.DeclaringType.Name + ".") : "") + item.Name;
			Patches patchInfo;
			try
			{
				patchInfo = Harmony.GetPatchInfo(item);
			}
			catch (Exception ex)
			{
				Main.Log.LogWarning((object)("ThorsKist: could not read patches on " + text + ": " + ex.Message));
				continue;
			}
			if (patchInfo == null)
			{
				continue;
			}
			Stake stake = StakeOf(item);
			foreach (Patch prefix in patchInfo.Prefixes)
			{
				if (!(prefix.owner == "posixone_ThorsKist"))
				{
					MethodInfo patchMethod = prefix.PatchMethod;
					if (!(patchMethod == null) && !(patchMethod.ReturnType != typeof(bool)))
					{
						Note(prefix, text, stake, replaces: true);
					}
				}
			}
			foreach (Patch transpiler in patchInfo.Transpilers)
			{
				if (!(transpiler.owner == "posixone_ThorsKist"))
				{
					Note(transpiler, text, stake, replaces: false);
				}
			}
		}
		Main.Log.LogInfo((object)("ThorsKist: conflict scan looked at " + num + " crafting seam(s), found " + _found.Count + " conflict(s)."));
	}

	private static void Note(Patch p, string method, Stake stake, bool replaces)
	{
		Conflict conflict = new Conflict
		{
			Mod = ModName(p),
			Method = method,
			Replaces = replaces,
			Stake = stake
		};
		if (_seen.Add(conflict.Key))
		{
			_found.Add(conflict);
			string text = "ThorsKist: " + conflict.Mod + (replaces ? " may take over " : " rewrites ") + method;
			switch (stake)
			{
			case Stake.Payment:
				Main.Log.LogWarning((object)(text + " -- if it does, the Kist can be counted toward a cost and never charged for it."));
				break;
			case Stake.Deposit:
				Main.Log.LogWarning((object)(text + " -- if it does, an Alt-click deposit may not reach the Kist. Crafting is unaffected."));
				break;
			default:
				Main.Log.LogInfo((object)(text + " -- Kist amounts on screen may be wrong; payment is unaffected."));
				break;
			}
		}
	}

	private static string ModName(Patch p)
	{
		string owner = p.owner;
		try
		{
			foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
			{
				if (pluginInfo.Value != null && pluginInfo.Value.Metadata != null && (pluginInfo.Key == owner || pluginInfo.Value.Metadata.GUID == owner))
				{
					return pluginInfo.Value.Metadata.Name;
				}
			}
		}
		catch
		{
		}
		try
		{
			if (p.PatchMethod != null && p.PatchMethod.DeclaringType != null)
			{
				string name = p.PatchMethod.DeclaringType.Assembly.GetName().Name;
				if (!string.IsNullOrEmpty(name))
				{
					return name;
				}
			}
		}
		catch
		{
		}
		if (!string.IsNullOrEmpty(owner))
		{
			return owner;
		}
		return "another mod";
	}

	public static string Report()
	{
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.Append("Another mod hooks the same game code Thor's Kist uses.\n");
		stringBuilder.Append("<i>This is a heads-up, not a fault. It may never affect you.</i>\n");
		Section(stringBuilder, Stake.Payment, "Could affect crafting", "If it takes over, the Kist may be counted toward a recipe and never charged for it. Setting <b>craftFromKist = false</b> stops the Kist offering materials it cannot charge for.");
		Section(stringBuilder, Stake.Deposit, "Could affect depositing", "If it takes over, an Alt-click may not reach the Kist. Crafting is unaffected, and the Kist window still deposits normally.");
		stringBuilder.Append("\nThe log line <b>\"Kist paid ...\"</b> appears whenever the vault actually pays, ");
		stringBuilder.Append("so you can always tell a real problem from materials simply coming from elsewhere.");
		return stringBuilder.ToString();
	}

	private static void Section(StringBuilder sb, Stake stake, string heading, string consequence)
	{
		bool flag = false;
		foreach (Conflict item in _found)
		{
			if (item.Stake == stake)
			{
				if (!flag)
				{
					sb.Append("\n<b>").Append(heading).Append("</b>\n");
					flag = true;
				}
				sb.Append("   ").Append(item.Mod).Append(' ')
					.Append(item.Replaces ? "may take over " : "rewrites ")
					.Append(item.Method)
					.Append('\n');
			}
		}
		if (flag)
		{
			sb.Append("   ").Append(consequence).Append('\n');
		}
	}
}
public static class KistDialog
{
	private static GameObject _root;

	private static TMP_FontAsset _font;

	private static string _pendingTitle;

	private static string _pendingBody;

	private static readonly Color PanelBg = new Color(0.07f, 0.065f, 0.06f, 0.98f);

	private static readonly Color Parchment = new Color(0.87f, 0.82f, 0.7f, 1f);

	private static readonly Color Warn = new Color(0.86f, 0.62f, 0.26f, 1f);

	private static readonly Color BtnBg = new Color(0.24f, 0.21f, 0.16f, 1f);

	public static bool IsOpen
	{
		get
		{
			if ((Object)(object)_root != (Object)null)
			{
				return _root.activeInHierarchy;
			}
			return false;
		}
	}

	private static float S => Mathf.Clamp(Main.UiScale.Value, 0.6f, 1.6f);

	public static void Show(string title, string body)
	{
		_pendingTitle = title;
		_pendingBody = body;
	}

	public static void Tick()
	{
		if ((Object)(object)Player.m_localPlayer == (Object)null)
		{
			if ((Object)(object)_root != (Object)null)
			{
				Close();
			}
			return;
		}
		if ((Object)(object)_root == (Object)null && _pendingBody != null)
		{
			Build();
		}
		if (IsOpen && (Input.GetKeyDown((KeyCode)27) || Input.GetKeyDown((KeyCode)13) || Input.GetKeyDown((KeyCode)271)))
		{
			Close();
		}
	}

	public static void Close()
	{
		if ((Object)(object)_root != (Object)null)
		{
			Object.Destroy((Object)(object)_root);
		}
		_root = null;
		_pendingTitle = null;
		_pendingBody = null;
	}

	private static void Build()
	{
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_0144: Unknown result type (might be due to invalid IL or missing references)
		//IL_017c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_01df: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0207: Unknown result type (might be due to invalid IL or missing references)
		//IL_0222: Unknown result type (might be due to invalid IL or missing references)
		//IL_022c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0235: Unknown result type (might be due to invalid IL or missing references)
		//IL_024e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0264: Unknown result type (might be due to invalid IL or missing references)
		//IL_026d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0298: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a2: Expected O, but got Unknown
		//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)InventoryGui.instance == (Object)null)
		{
			return;
		}
		Canvas componentInParent = ((Component)InventoryGui.instance).GetComponentInParent<Canvas>();
		if (!((Object)(object)componentInParent == (Object)null))
		{
			Transform transform = ((Component)(((Object)(object)componentInParent.rootCanvas != (Object)null) ? componentInParent.rootCanvas : componentInParent)).transform;
			_font = FindFont();
			RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
			float num;
			if (!((Object)(object)val != (Object)null))
			{
				num = Screen.width;
			}
			else
			{
				Rect rect = val.rect;
				num = ((Rect)(ref rect)).width;
			}
			float num2 = num;
			float num3 = Mathf.Round(16f * S);
			float num4 = Mathf.Min(700f * S, num2 * 0.7f);
			float size = 18f * S;
			float size2 = 13f * S;
			float num5 = Mathf.Round(30f * S);
			float num6 = Mathf.Round(26f * S);
			_root = NewPanel("ThorsKist_Dialog", transform, PanelBg);
			_root.transform.SetAsLastSibling();
			TMP_Text val2 = NewText(_root.transform, _pendingTitle ?? "THOR'S KIST", size, Warn, (TextAlignmentOptions)513);
			TMP_Text obj = NewText(_root.transform, _pendingBody ?? "", size2, Parchment, (TextAlignmentOptions)257);
			obj.textWrappingMode = (TextWrappingModes)1;
			obj.overflowMode = (TextOverflowModes)0;
			float num7 = num4 - num3 * 2f;
			float y = obj.GetPreferredValues(obj.text, num7, 0f).y;
			float num8 = num3 + num6 + num3 * 0.5f + y + num3 + num5 + num3;
			Transform transform2 = _root.transform;
			Transform obj2 = ((transform2 is RectTransform) ? transform2 : null);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0.5f, 0.5f);
			((RectTransform)obj2).anchorMax = val3;
			((RectTransform)obj2).anchorMin = val3;
			((RectTransform)obj2).pivot = new Vector2(0.5f, 0.5f);
			((RectTransform)obj2).sizeDelta = new Vector2(num4, num8);
			((RectTransform)obj2).anchoredPosition = Vector2.zero;
			AddOutline(_root, Warn);
			Place(val2.rectTransform, new Vector2(0f, 1f), new Vector2(num3, 0f - num3), new Vector2(num7, num6));
			Place(obj.rectTransform, new Vector2(0f, 1f), new Vector2(num3, 0f - (num3 + num6 + num3 * 0.5f)), new Vector2(num7, y));
			Transform transform3 = ((Component)NewButton(_root.transform, "OK", 13f * S, new UnityAction(Close))).transform;
			Place((RectTransform)(object)((transform3 is RectTransform) ? transform3 : null), new Vector2(0.5f, 0f), new Vector2(0f, num3), new Vector2(Mathf.Round(90f * S), num5), new Vector2(0.5f, 0f));
		}
	}

	private static TMP_FontAsset FindFont()
	{
		if ((Object)(object)InventoryGui.instance != (Object)null)
		{
			TMP_Text componentInChildren = ((Component)InventoryGui.instance).GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)componentInChildren != (Object)null && (Object)(object)componentInChildren.font != (Object)null)
			{
				return componentInChildren.font;
			}
		}
		return null;
	}

	private static GameObject NewPanel(string name, Transform parent, Color bg)
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Expected O, but got Unknown
		GameObject val = new GameObject(name, new Type[3]
		{
			typeof(RectTransform),
			typeof(CanvasRenderer),
			typeof(Image)
		});
		val.transform.SetParent(parent, false);
		Image component = val.GetComponent<Image>();
		((Graphic)component).color = bg;
		((Graphic)component).raycastTarget = bg.a > 0.01f;
		return val;
	}

	private static void AddOutline(GameObject go, Color colour)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		Outline obj = go.AddComponent<Outline>();
		((Shadow)obj).effectColor = new Color(colour.r, colour.g, colour.b, 0.55f);
		((Shadow)obj).effectDistance = new Vector2(2f, -2f);
	}

	private static TMP_Text NewText(Transform parent, string text, float size, Color colour, TextAlignmentOptions align)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject("text", new Type[1] { typeof(RectTransform) });
		val.transform.SetParent(parent, false);
		TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
		if ((Object)(object)_font != (Object)null)
		{
			((TMP_Text)val2).font = _font;
		}
		((TMP_Text)val2).text = text;
		((TMP_Text)val2).fontSize = size;
		((Graphic)val2).color = colour;
		((TMP_Text)val2).alignment = align;
		((TMP_Text)val2).richText = true;
		((Graphic)val2).raycastTarget = false;
		return (TMP_Text)(object)val2;
	}

	private static Button NewButton(Transform parent, string label, float size, UnityAction onClick)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = NewPanel("button", parent, BtnBg);
		Button obj = val.AddComponent<Button>();
		((Selectable)obj).targetGraphic = (Graphic)(object)val.GetComponent<Image>();
		ColorBlock colors = ((Selectable)obj).colors;
		((ColorBlock)(ref colors)).normalColor = Color.white;
		((ColorBlock)(ref colors)).highlightedColor = new Color(1.25f, 1.25f, 1.25f, 1f);
		((ColorBlock)(ref colors)).pressedColor = new Color(0.8f, 0.8f, 0.8f, 1f);
		((Selectable)obj).colors = colors;
		((UnityEvent)obj.onClick).AddListener(onClick);
		RectTransform rectTransform = NewText(val.transform, label, size, Parchment, (TextAlignmentOptions)514).rectTransform;
		rectTransform.anchorMin = Vector2.zero;
		rectTransform.anchorMax = Vector2.one;
		rectTransform.offsetMin = new Vector2(2f, 1f);
		rectTransform.offsetMax = new Vector2(-2f, -1f);
		return obj;
	}

	private static void Place(RectTransform rt, Vector2 anchor, Vector2 offset, Vector2 size)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		Place(rt, anchor, offset, size, new Vector2(anchor.x, anchor.y));
	}

	private static void Place(RectTransform rt, Vector2 anchor, Vector2 offset, Vector2 size, Vector2 pivot)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		Vector2 anchorMin = (rt.anchorMax = anchor);
		rt.anchorMin = anchorMin;
		rt.pivot = pivot;
		rt.sizeDelta = size;
		rt.anchoredPosition = offset;
	}
}
public static class KistFx
{
	private static bool _warnedDepositSfx;

	private static bool _warnedDepositVfx;

	private static bool _warnedAnchorSfx;

	private static bool _warnedAnchorVfx;

	private static bool _warnedAnchorFall;

	public static void Deposit()
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.m_localPlayer;
		if (!((Object)(object)localPlayer == (Object)null))
		{
			DepositAt(((Character)localPlayer).GetCenterPoint());
		}
	}

	public static void DepositAt(Vector3 pos)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		if (Main.DepositSound.Value)
		{
			Spawn(Main.DepositSfxPrefab.Value, pos, Main.DepositVolume.Value, ref _warnedDepositSfx);
		}
		if (Main.DepositVfx.Value)
		{
			Spawn(Main.DepositVfxPrefab.Value, pos, 1f, ref _warnedDepositVfx);
		}
	}

	public static void Take()
	{
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		if (Main.TakeSound.Value)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && ((Humanoid)localPlayer).m_pickupEffects != null && ((Humanoid)localPlayer).m_pickupEffects.HasEffects())
			{
				Amplify(((Humanoid)localPlayer).m_pickupEffects.Create(((Component)localPlayer).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)), Main.TakeVolume.Value);
			}
		}
	}

	public static void Anchor(Vector3 groundPos)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		if (Main.AnchorEffects.Value && !((Object)(object)Main.Instance == (Object)null))
		{
			((MonoBehaviour)Main.Instance).StartCoroutine(Slam(groundPos));
		}
	}

	private static IEnumerator Slam(Vector3 groundPos)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		float height = Mathf.Max(1f, Main.AnchorDropHeight.Value);
		float fall = Mathf.Max(0.05f, Main.AnchorDropSeconds.Value);
		GameObject falling = Spawn(Main.AnchorFallVfxPrefab.Value, groundPos + Vector3.up * height, 1f, ref _warnedAnchorFall);
		float t = 0f;
		while (t < fall)
		{
			t += Time.deltaTime;
			if ((Object)(object)falling != (Object)null)
			{
				float num = Mathf.Clamp01(t / fall);
				falling.transform.position = Vector3.Lerp(groundPos + Vector3.up * height, groundPos, num * num);
			}
			yield return null;
		}
		if ((Object)(object)falling != (Object)null)
		{
			Object.Destroy((Object)(object)falling);
		}
		Spawn(Main.AnchorVfxPrefab.Value, groundPos, 1f, ref _warnedAnchorVfx);
		Spawn(Main.AnchorSfxPrefab.Value, groundPos, Main.AnchorVolume.Value, ref _warnedAnchorSfx);
	}

	private static GameObject Spawn(string prefabName, Vector3 pos, float volume, ref bool warned)
	{
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		if (string.IsNullOrEmpty(prefabName))
		{
			return null;
		}
		if ((Object)(object)ZNetScene.instance == (Object)null)
		{
			return null;
		}
		GameObject prefab = ZNetScene.instance.GetPrefab(prefabName);
		if ((Object)(object)prefab == (Object)null)
		{
			if (!warned)
			{
				warned = true;
				Main.Log.LogWarning((object)("ThorsKist: no prefab named '" + prefabName + "'. Check the Feedback section of the config."));
			}
			return null;
		}
		GameObject obj = Object.Instantiate<GameObject>(prefab, pos, Quaternion.identity);
		Amplify(obj, volume);
		return obj;
	}

	private static void Amplify(GameObject[] objects, float volume)
	{
		if (objects != null)
		{
			for (int i = 0; i < objects.Length; i++)
			{
				Amplify(objects[i], volume);
			}
		}
	}

	private static void Amplify(GameObject go, float volume)
	{
		if (!((Object)(object)go == (Object)null) && !Mathf.Approximately(volume, 1f))
		{
			ZSFX[] componentsInChildren = go.GetComponentsInChildren<ZSFX>(true);
			foreach (ZSFX obj in componentsInChildren)
			{
				obj.SetVolumeModifier(obj.GetVolumeModifier() * volume);
			}
		}
	}
}
public static class KistMap
{
	private static PinData _pin;

	private static Vector3 _pinnedAt;

	private static string _pinnedName;

	private static float _nextSync;

	private static bool _removingOurOwn;

	private static Sprite _icon;

	private static bool _iconResolved;

	public static bool IsProtected(PinData pin)
	{
		if (!_removingOurOwn && pin != null)
		{
			return pin == _pin;
		}
		return false;
	}

	public static void Forget()
	{
		_pin = null;
	}

	public static void Tick()
	{
		if (!(Time.realtimeSinceStartup < _nextSync))
		{
			_nextSync = Time.realtimeSinceStartup + 1f;
			Sync();
		}
	}

	public static void Sync()
	{
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)Minimap.instance == (Object)null)
		{
			_pin = null;
			return;
		}
		if (!Main.MapPin.Value || !KistClient.InHird)
		{
			Drop();
			return;
		}
		Vector3 anchor = KistClient.Hird.Anchor;
		string name = KistClient.Hird.Name;
		if (_pin != null && _pinnedAt == anchor && _pinnedName == name)
		{
			return;
		}
		Drop();
		_pin = Minimap.instance.AddPin(anchor, (PinType)3, name, false, false, 0L, default(PlatformUserID));
		if (_pin != null)
		{
			Sprite val = ChestIcon();
			if ((Object)(object)val != (Object)null)
			{
				_pin.m_icon = val;
			}
		}
		_pinnedAt = anchor;
		_pinnedName = name;
	}

	private static void Drop()
	{
		if (_pin == null)
		{
			return;
		}
		if ((Object)(object)Minimap.instance != (Object)null)
		{
			_removingOurOwn = true;
			try
			{
				Minimap.instance.RemovePin(_pin);
			}
			finally
			{
				_removingOurOwn = false;
			}
		}
		_pin = null;
	}

	private static Sprite ChestIcon()
	{
		if (_iconResolved)
		{
			return _icon;
		}
		if ((Object)(object)ZNetScene.instance == (Object)null)
		{
			return null;
		}
		_iconResolved = true;
		string[] array = new string[6]
		{
			Main.MapPinPrefab.Value,
			"piece_chest_wood",
			"piece_chest_treasure",
			"piece_chest",
			"piece_chest_private",
			"piece_chest_blackmetal"
		};
		foreach (string text in array)
		{
			if (string.IsNullOrEmpty(text))
			{
				continue;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(text);
			if (!((Object)(object)prefab == (Object)null))
			{
				Piece component = prefab.GetComponent<Piece>();
				if (!((Object)(object)component == (Object)null) && !((Object)(object)component.m_icon == (Object)null))
				{
					_icon = component.m_icon;
					Main.Log.LogInfo((object)("ThorsKist: map pin using the icon from '" + text + "'."));
					return _icon;
				}
			}
		}
		Main.Log.LogWarning((object)"ThorsKist: found no chest prefab for the map pin icon; falling back to a plain map pin.");
		return null;
	}
}
public static class KistServer
{
	private class Vault
	{
		public Hird Hird;

		public Inventory Inventory;
	}

	private static readonly List<Vault> Vaults = new List<Vault>();

	private static readonly Dictionary<long, long> PeerByPlayer = new Dictionary<long, long>();

	private static readonly Dictionary<long, string> NameByPlayer = new Dictionary<long, string>();

	private static readonly Dictionary<long, Vector3> PosByPlayer = new Dictionary<long, Vector3>();

	private static readonly Dictionary<long, float> PosSeenAt = new Dictionary<long, float>();

	private static bool _loaded;

	private static bool _dirty;

	private static float _nextSave;

	private const string VaultFolder = "ThorsKist";

	public static bool IsServer
	{
		get
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				return ZNet.instance.IsServer();
			}
			return false;
		}
	}

	private static string SaveDir
	{
		get
		{
			string text = Utils.GetSaveDataPath((FileSource)2);
			if (string.IsNullOrEmpty(text))
			{
				text = Paths.ConfigPath;
			}
			return Path.Combine(text, "ThorsKist");
		}
	}

	private static string LegacySaveDir => Path.Combine(Paths.ConfigPath, "ThorsKist");

	private static string VaultFileName => "kist_" + (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetWorldUID() : 0) + ".dat";

	private static string SavePath => Path.Combine(SaveDir, VaultFileName);

	private static void MigrateLegacyVault()
	{
		try
		{
			string savePath = SavePath;
			if (!File.Exists(savePath))
			{
				string text = Path.Combine(LegacySaveDir, VaultFileName);
				if (File.Exists(text))
				{
					Directory.CreateDirectory(SaveDir);
					File.Copy(text, savePath, overwrite: false);
					File.Delete(text);
					Main.Log.LogWarning((object)("ThorsKist: moved the vault out of BepInEx/config, which server managers wipe on update.\n  from: " + text + "\n  to:   " + savePath));
				}
			}
		}
		catch (Exception ex)
		{
			Main.Log.LogError((object)("ThorsKist: could not move the old vault file: " + ex));
		}
	}

	public static void EnsureLoaded()
	{
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Expected O, but got Unknown
		if (_loaded || !IsServer || (Object)(object)ObjectDB.instance == (Object)null)
		{
			return;
		}
		_loaded = true;
		try
		{
			MigrateLegacyVault();
			string savePath = SavePath;
			if (!File.Exists(savePath))
			{
				Main.Log.LogInfo((object)("ThorsKist: no vault file yet at " + savePath));
				return;
			}
			ZPackage val = new ZPackage(File.ReadAllBytes(savePath));
			int num = val.ReadInt();
			if (num != 1)
			{
				Main.Log.LogError((object)("ThorsKist: vault file is protocol " + num + ", this build speaks " + 1 + ". Refusing to load it."));
				return;
			}
			int num2 = val.ReadInt();
			for (int i = 0; i < num2; i++)
			{
				Hird hird = Hird.ReadHeader(val);
				ZPackage val2 = val.ReadPackage();
				Vault vault = new Vault
				{
					Hird = hird,
					Inventory = NewInventory(hird)
				};
				vault.Inventory.Load(val2);
				Vaults.Add(vault);
			}
			Main.Log.LogInfo((object)("ThorsKist: loaded " + Vaults.Count + " hird(s) from " + savePath));
		}
		catch (Exception ex)
		{
			Main.Log.LogError((object)("ThorsKist: could not load the vault file: " + ex));
		}
	}

	private static Inventory NewInventory(Hird hird)
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Expected O, but got Unknown
		return new Inventory("Kist_" + hird.Id, (Sprite)null, 8, Mathf.Clamp(Main.KistRows.Value, 4, 256));
	}

	private static void Save()
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Expected O, but got Unknown
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Expected O, but got Unknown
		if (!IsServer)
		{
			return;
		}
		try
		{
			Directory.CreateDirectory(SaveDir);
			ZPackage val = new ZPackage();
			val.Write(1);
			val.Write(Vaults.Count);
			foreach (Vault vault in Vaults)
			{
				vault.Hird.WriteHeader(val);
				ZPackage val2 = new ZPackage();
				vault.Inventory.Save(val2);
				val.Write(val2);
			}
			string savePath = SavePath;
			string text = savePath + ".tmp";
			File.WriteAllBytes(text, val.GetArray());
			if (File.Exists(savePath))
			{
				File.Delete(savePath);
			}
			File.Move(text, savePath);
			_dirty = false;
		}
		catch (Exception ex)
		{
			Main.Log.LogError((object)("ThorsKist: could not save the vault file: " + ex));
		}
	}

	private static void MarkDirty()
	{
		_dirty = true;
		_nextSave = Time.realtimeSinceStartup + 2f;
	}

	public static void Tick()
	{
		if (IsServer)
		{
			EnsureLoaded();
			if (_dirty && Time.realtimeSinceStartup >= _nextSave)
			{
				Save();
			}
		}
	}

	public static void FlushNow()
	{
		if (_dirty)
		{
			Save();
		}
	}

	public static void Reset()
	{
		FlushNow();
		Vaults.Clear();
		PeerByPlayer.Clear();
		NameByPlayer.Clear();
		PosByPlayer.Clear();
		PosSeenAt.Clear();
		_loaded = false;
		_dirty = false;
	}

	private static Vault VaultOf(long playerId)
	{
		foreach (Vault vault in Vaults)
		{
			if (vault.Hird.IsMember(playerId))
			{
				return vault;
			}
		}
		return null;
	}

	private static Vault VaultById(string hirdId)
	{
		foreach (Vault vault in Vaults)
		{
			if (vault.Hird.Id == hirdId)
			{
				return vault;
			}
		}
		return null;
	}

	private static Vault VaultWithInviteFor(long playerId)
	{
		foreach (Vault vault in Vaults)
		{
			if (vault.Hird.IsInvited(playerId))
			{
				return vault;
			}
		}
		return null;
	}

	public static void OnMessage(long senderPeer, ZPackage pkg)
	{
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Unknown result type (might be due to invalid IL or missing references)
		//IL_017f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0195: Unknown result type (might be due to invalid IL or missing references)
		//IL_019d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		if (!IsServer)
		{
			return;
		}
		EnsureLoaded();
		try
		{
			if (pkg.ReadInt() != 4)
			{
				Deny(senderPeer, "Your Thor's Kist version does not match the server's.");
				return;
			}
			Wire.C2S c2S = (Wire.C2S)pkg.ReadByte();
			long num = pkg.ReadLong();
			string text = pkg.ReadString();
			Vector3 val = pkg.ReadVector3();
			if (num != 0L)
			{
				PeerByPlayer[num] = senderPeer;
				if (!string.IsNullOrEmpty(text))
				{
					NameByPlayer[num] = text;
				}
				PosByPlayer[num] = val;
				PosSeenAt[num] = Time.realtimeSinceStartup;
			}
			switch (c2S)
			{
			case Wire.C2S.Hello:
				BroadcastRoster();
				SendState(num);
				break;
			case Wire.C2S.RequestState:
				SendState(num);
				break;
			case Wire.C2S.CreateHird:
				CreateHird(senderPeer, num, text, val, pkg.ReadString());
				break;
			case Wire.C2S.Invite:
				Invite(senderPeer, num, text, pkg.ReadLong(), pkg.ReadString());
				break;
			case Wire.C2S.AcceptInvite:
				AcceptInvite(senderPeer, num, text);
				break;
			case Wire.C2S.DeclineInvite:
				DeclineInvite(num);
				break;
			case Wire.C2S.Kick:
				Kick(senderPeer, num, pkg.ReadLong());
				break;
			case Wire.C2S.Leave:
				Leave(num);
				break;
			case Wire.C2S.Rename:
				Rename(senderPeer, num, pkg.ReadString());
				break;
			case Wire.C2S.Disband:
				Disband(senderPeer, num);
				break;
			case Wire.C2S.AnchorQuery:
				SendAnchorStatus(senderPeer, num, val);
				break;
			case Wire.C2S.SetAnchor:
				SetAnchor(senderPeer, num, val);
				break;
			case Wire.C2S.Deposit:
				Deposit(senderPeer, num, text, val, pkg.ReadInt(), pkg.ReadPackage());
				break;
			case Wire.C2S.Take:
				Take(senderPeer, num, val, pkg.ReadInt(), pkg.ReadVector2i(), pkg.ReadString(), pkg.ReadInt());
				break;
			case Wire.C2S.Diag:
				Main.Log.LogInfo((object)("ThorsKist [" + text + "] " + pkg.ReadString()));
				break;
			case Wire.C2S.ConsumeForCraft:
				ConsumeForCraft(senderPeer, num, val, pkg);
				break;
			case Wire.C2S.Ping:
				break;
			}
		}
		catch (Exception ex)
		{
			Main.Log.LogError((object)("ThorsKist: bad message from peer " + senderPeer + ": " + ex));
		}
	}

	private static void CreateHird(long peer, long playerId, string playerName, Vector3 pos, string name)
	{
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		if (VaultOf(playerId) != null)
		{
			Deny(peer, "You already belong to a hird.");
			return;
		}
		if (string.IsNullOrEmpty(name))
		{
			name = playerName + "'s hird";
		}
		if (name.Length > 40)
		{
			name = name.Substring(0, 40);
		}
		Hird hird = new Hird
		{
			Id = Guid.NewGuid().ToString("N"),
			Name = name,
			OwnerId = playerId,
			Anchor = pos
		};
		hird.Members.Add(new HirdMember(playerId, playerName));
		Vault vault = new Vault
		{
			Hird = hird,
			Inventory = NewInventory(hird)
		};
		Vaults.Add(vault);
		MarkDirty();
		AnnounceAnchor(vault, 0L);
		Toast(peer, "Hird \"" + name + "\" founded. Invite at least " + (Main.MinHirdSize.Value - 1) + " more to unlock the Kist.");
		SendState(playerId);
	}

	private static void Invite(long peer, long playerId, string playerName, long targetId, string targetName)
	{
		Vault vault = VaultOf(playerId);
		if (vault == null)
		{
			Deny(peer, "You are not in a hird.");
			return;
		}
		if (vault.Hird.OwnerId != playerId)
		{
			Deny(peer, "Only the hird's founder may invite.");
			return;
		}
		if (vault.Hird.IsMember(targetId))
		{
			Deny(peer, targetName + " is already in your hird.");
			return;
		}
		if (VaultOf(targetId) != null)
		{
			Deny(peer, targetName + " already belongs to another hird.");
			return;
		}
		if (vault.Hird.IsInvited(targetId))
		{
			Deny(peer, targetName + " has already been invited.");
			return;
		}
		vault.Hird.Invites.Add(new HirdInvite
		{
			PlayerId = targetId,
			Name = targetName,
			FromPlayerId = playerId,
			FromName = playerName
		});
		MarkDirty();
		Toast(peer, "Invited " + targetName + ".");
		SendStateToHird(vault);
	}

	private static void AcceptInvite(long peer, long playerId, string playerName)
	{
		Vault vault = VaultWithInviteFor(playerId);
		if (vault == null)
		{
			Deny(peer, "That invitation is no longer open.");
			return;
		}
		if (VaultOf(playerId) != null)
		{
			Deny(peer, "You already belong to a hird.");
			return;
		}
		vault.Hird.Invites.RemoveAll((HirdInvite i) => i.PlayerId == playerId);
		vault.Hird.Members.Add(new HirdMember(playerId, playerName));
		MarkDirty();
		SendStateToHird(vault);
		ToastHird(vault, playerName + " has joined the hird.");
	}

	private static void DeclineInvite(long playerId)
	{
		Vault vault = VaultWithInviteFor(playerId);
		if (vault != null)
		{
			vault.Hird.Invites.RemoveAll((HirdInvite i) => i.PlayerId == playerId);
			MarkDirty();
			SendStateToHird(vault);
			SendState(playerId);
		}
	}

	private static void Kick(long peer, long playerId, long targetId)
	{
		Vault vault = VaultOf(playerId);
		if (vault == null)
		{
			Deny(peer, "You are not in a hird.");
			return;
		}
		if (vault.Hird.OwnerId != playerId)
		{
			Deny(peer, "Only the hird's founder may remove members.");
			return;
		}
		if (targetId == playerId)
		{
			Deny(peer, "Disband the hird instead.");
			return;
		}
		string text = vault.Hird.MemberName(targetId);
		vault.Hird.Members.RemoveAll((HirdMember m) => m.PlayerId == targetId);
		vault.Hird.Invites.RemoveAll((HirdInvite i) => i.PlayerId == targetId);
		MarkDirty();
		ToastHird(vault, text + " was removed from the hird.");
		SendStateToHird(vault);
		SendState(targetId);
	}

	private static void Leave(long playerId)
	{
		Vault vault = VaultOf(playerId);
		if (vault == null)
		{
			return;
		}
		if (vault.Hird.OwnerId == playerId)
		{
			vault.Hird.Members.RemoveAll((HirdMember m) => m.PlayerId == playerId);
			if (vault.Hird.Members.Count > 0)
			{
				vault.Hird.OwnerId = vault.Hird.Members[0].PlayerId;
			}
		}
		else
		{
			vault.Hird.Members.RemoveAll((HirdMember m) => m.PlayerId == playerId);
		}
		MarkDirty();
		SendStateToHird(vault);
		SendState(playerId);
	}

	private static void Rename(long peer, long playerId, string name)
	{
		Vault vault = VaultOf(playerId);
		if (vault == null)
		{
			Deny(peer, "You are not in a hird.");
			return;
		}
		if (vault.Hird.OwnerId != playerId)
		{
			Deny(peer, "Only the hird's founder may rename it.");
			return;
		}
		name = (name ?? "").Trim();
		if (name.Length == 0)
		{
			Deny(peer, "A hird needs a name.");
			return;
		}
		if (name.Length > 40)
		{
			name = name.Substring(0, 40);
		}
		if (!(name == vault.Hird.Name))
		{
			string name2 = vault.Hird.Name;
			vault.Hird.Name = name;
			MarkDirty();
			ToastHird(vault, "\"" + name2 + "\" is now \"" + name + "\".");
			SendStateToHird(vault);
		}
	}

	private static void Disband(long peer, long playerId)
	{
		Vault vault = VaultOf(playerId);
		if (vault == null)
		{
			return;
		}
		if (vault.Hird.OwnerId != playerId)
		{
			Deny(peer, "Only the hird's founder may disband it.");
			return;
		}
		if (vault.Inventory.NrOfItems() > 0)
		{
			Deny(peer, "Empty the Kist before disbanding -- " + vault.Inventory.NrOfItems() + " stack(s) would be destroyed.");
			return;
		}
		List<HirdMember> list = new List<HirdMember>(vault.Hird.Members);
		Vaults.Remove(vault);
		MarkDirty();
		foreach (HirdMember item in list)
		{
			SendState(item.PlayerId);
		}
	}

	private static bool MayReanchor(Vault v, Vector3 at, out string why)
	{
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		why = null;
		int num = 0;
		foreach (HirdMember member in v.Hird.Members)
		{
			if (IsConnected(member.PlayerId))
			{
				num++;
				if (!PosByPlayer.TryGetValue(member.PlayerId, out var value) || !PosSeenAt.TryGetValue(member.PlayerId, out var value2) || Time.realtimeSinceStartup - value2 > 20f)
				{
					why = "Still waiting to hear where " + member.Name + " is.";
					return false;
				}
				float num2 = Vector3.Distance(value, at);
				if (num2 > Main.AnchorRadius.Value)
				{
					why = member.Name + " is " + Mathf.RoundToInt(num2) + "m away. Everyone must be within " + Mathf.RoundToInt(Main.AnchorRadius.Value) + "m.";
					return false;
				}
			}
		}
		if (num < Main.MinHirdSize.Value)
		{
			why = "The hird must be gathered: " + num + " of " + Main.MinHirdSize.Value + " online.";
			return false;
		}
		return true;
	}

	private static void SendAnchorStatus(long peer, long playerId, Vector3 at)
	{
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		Vault vault = VaultOf(playerId);
		ZPackage val = NewReply(Wire.S2C.AnchorStatus);
		if (vault == null || vault.Hird.OwnerId != playerId)
		{
			val.Write(false);
			val.Write((vault == null) ? "You are not in a hird." : "Only the hird's founder may move the Kist.");
		}
		else
		{
			string why;
			bool flag = MayReanchor(vault, at, out why);
			val.Write(flag);
			val.Write(flag ? "" : why);
		}
		val.Write(Main.AnchorCostPrefab.Value ?? "");
		val.Write(Main.AnchorCostAmount.Value);
		SendToPeer(peer, val);
	}

	private static void SetAnchor(long peer, long playerId, Vector3 pos)
	{
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		Vault vault = VaultOf(playerId);
		if (vault == null)
		{
			Deny(peer, "You are not in a hird.");
			return;
		}
		if (vault.Hird.OwnerId != playerId)
		{
			Deny(peer, "Only the hird's founder may move the Kist.");
			return;
		}
		if (!MayReanchor(vault, pos, out var why))
		{
			Deny(peer, why);
			return;
		}
		vault.Hird.Anchor = pos;
		MarkDirty();
		ToastHird(vault, "The Kist has been anchored to a new location.");
		AnnounceAnchor(vault, playerId);
		SendStateToHird(vault);
	}

	private static bool Usable(Vault v, Vector3 pos, float radius, out string why)
	{
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		why = null;
		if (v.Hird.Members.Count < Main.MinHirdSize.Value)
		{
			why = "The Kist stays shut until the hird has " + Main.MinHirdSize.Value + " members.";
			return false;
		}
		if (Main.UnlimitedRange.Value)
		{
			return true;
		}
		float num = Vector3.Distance(pos, v.Hird.Anchor);
		if (num > radius)
		{
			why = "Too far from the Kist (" + Mathf.RoundToInt(num) + "m of " + Mathf.RoundToInt(radius) + "m).";
			return false;
		}
		return true;
	}

	private static void Deposit(long peer, long playerId, string playerName, Vector3 pos, int token, ZPackage items)
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		Vault vault = VaultOf(playerId);
		if (vault == null)
		{
			Reject(peer, token, "You are not in a hird.");
			return;
		}
		if (!Usable(vault, pos, Main.DepositRadius.Value, out var why))
		{
			Reject(peer, token, why);
			return;
		}
		List<ItemData> list = ItemWire.ReadItems(items);
		if (list.Count == 0)
		{
			Reject(peer, token, "Nothing to store.");
			return;
		}
		foreach (ItemData item in list)
		{
			if (!vault.Inventory.CanAddItem(item, item.m_stack))
			{
				Reject(peer, token, "The Kist is full.");
				return;
			}
		}
		foreach (ItemData item2 in list)
		{
			vault.Inventory.AddItem(item2);
		}
		MarkDirty();
		Ack(peer, token);
		AnnounceDeposit(vault, playerId, pos);
		SendStateToHird(vault);
	}

	private static void Take(long peer, long playerId, Vector3 pos, int token, Vector2i slot, string expectName, int amount)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Expected O, but got Unknown
		Vault vault = VaultOf(playerId);
		if (vault == null)
		{
			Reject(peer, token, "You are not in a hird.");
			return;
		}
		if (!Usable(vault, pos, Main.TakeRadius.Value, out var why))
		{
			Reject(peer, token, why);
			return;
		}
		ItemData itemAt = vault.Inventory.GetItemAt(slot.x, slot.y);
		if (itemAt == null)
		{
			Reject(peer, token, "That item is already gone.");
			return;
		}
		if (itemAt.m_shared.m_name != expectName)
		{
			Reject(peer, token, "That item is already gone.");
			return;
		}
		if (amount <= 0 || amount > itemAt.m_stack)
		{
			amount = itemAt.m_stack;
		}
		ItemData val = itemAt.Clone();
		val.m_stack = amount;
		if (amount >= itemAt.m_stack)
		{
			vault.Inventory.RemoveItem(itemAt);
		}
		else
		{
			itemAt.m_stack -= amount;
		}
		MarkDirty();
		ZPackage val2 = NewReply(Wire.S2C.TakeResult);
		val2.Write(token);
		ZPackage val3 = new ZPackage();
		ItemWire.WriteItem(val3, val);
		val2.Write(val3);
		SendToPeer(peer, val2);
		SendStateToHird(vault);
	}

	private static void ConsumeForCraft(long peer, long playerId, Vector3 pos, ZPackage pkg)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		Vault vault = VaultOf(playerId);
		if (vault == null)
		{
			return;
		}
		if (!Usable(vault, pos, Main.CraftRadius.Value, out var why))
		{
			Main.Log.LogWarning((object)("ThorsKist: refused a craft consume from player " + playerId + " -- " + why));
			return;
		}
		int num = pkg.ReadInt();
		bool flag = false;
		for (int i = 0; i < num; i++)
		{
			string text = pkg.ReadString();
			int num2 = pkg.ReadInt();
			int quality = pkg.ReadInt();
			if (num2 > 0)
			{
				int num3 = KistStock.Count(vault.Inventory, text, quality);
				if (num3 < num2)
				{
					Main.Log.LogWarning((object)("ThorsKist: craft consume short by " + (num2 - num3) + " x " + text));
					num2 = num3;
				}
				if (num2 > 0)
				{
					KistStock.Take(vault.Inventory, text, num2, quality);
					flag = true;
				}
			}
		}
		if (flag)
		{
			MarkDirty();
			SendStateToHird(vault);
		}
	}

	private static ZPackage NewReply(Wire.S2C op)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Expected O, but got Unknown
		ZPackage val = new ZPackage();
		val.Write(4);
		val.Write((byte)op);
		return val;
	}

	private static void SendToPeer(long peer, ZPackage pkg)
	{
		if (ZRoutedRpc.instance != null)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(peer, "ThorsKist_S2C", new object[1] { pkg });
		}
	}

	private static void SendToPlayer(long playerId, ZPackage pkg)
	{
		if (PeerByPlayer.TryGetValue(playerId, out var value))
		{
			SendToPeer(value, pkg);
		}
	}

	private static void Deny(long peer, string message)
	{
		ZPackage val = NewReply(Wire.S2C.Denied);
		val.Write(message);
		SendToPeer(peer, val);
	}

	private static void Toast(long peer, string message)
	{
		ZPackage val = NewReply(Wire.S2C.Toast);
		val.Write(message);
		SendToPeer(peer, val);
	}

	private static void ToastHird(Vault v, string message)
	{
		foreach (HirdMember member in v.Hird.Members)
		{
			ZPackage val = NewReply(Wire.S2C.Toast);
			val.Write(message);
			SendToPlayer(member.PlayerId, val);
		}
	}

	private static void AnnounceAnchor(Vault v, long payerPlayerId)
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		foreach (HirdMember member in v.Hird.Members)
		{
			ZPackage val = NewReply(Wire.S2C.AnchorSet);
			val.Write(v.Hird.Anchor);
			val.Write(payerPlayerId);
			SendToPlayer(member.PlayerId, val);
		}
	}

	private static void AnnounceDeposit(Vault v, long byPlayerId, Vector3 at)
	{
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		foreach (HirdMember member in v.Hird.Members)
		{
			if (member.PlayerId != byPlayerId)
			{
				ZPackage val = NewReply(Wire.S2C.DepositFx);
				val.Write(at);
				SendToPlayer(member.PlayerId, val);
			}
		}
	}

	private static void Ack(long peer, int token)
	{
		ZPackage val = NewReply(Wire.S2C.DepositAck);
		val.Write(token);
		SendToPeer(peer, val);
	}

	private static void Reject(long peer, int token, string why)
	{
		ZPackage val = NewReply(Wire.S2C.DepositRejected);
		val.Write(token);
		val.Write(why ?? "");
		SendToPeer(peer, val);
	}

	private static void SendState(long playerId)
	{
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bc: Expected O, but got Unknown
		Vault vault = VaultOf(playerId);
		ZPackage val = NewReply(Wire.S2C.State);
		if (vault == null)
		{
			val.Write(false);
			Vault vault2 = VaultWithInviteFor(playerId);
			if (vault2 != null)
			{
				val.Write(true);
				val.Write(vault2.Hird.Name);
				HirdInvite hirdInvite = vault2.Hird.Invites.Find((HirdInvite i) => i.PlayerId == playerId);
				val.Write((hirdInvite != null) ? hirdInvite.FromName : "");
			}
			else
			{
				val.Write(false);
			}
		}
		else
		{
			val.Write(true);
			vault.Hird.WriteHeader(val);
			val.Write(Main.MinHirdSize.Value);
			ZPackage val2 = new ZPackage();
			vault.Inventory.Save(val2);
			val.Write(val2);
		}
		WriteRoster(val, playerId);
		SendToPlayer(playerId, val);
	}

	private static void WriteRoster(ZPackage pkg, long forPlayer)
	{
		List<KeyValuePair<long, string>> list = new List<KeyValuePair<long, string>>();
		foreach (KeyValuePair<long, string> item in NameByPlayer)
		{
			if (item.Key != forPlayer && IsConnected(item.Key) && VaultOf(item.Key) == null)
			{
				list.Add(item);
			}
		}
		pkg.Write(list.Count);
		foreach (KeyValuePair<long, string> item2 in list)
		{
			pkg.Write(item2.Key);
			pkg.Write(item2.Value);
		}
	}

	private static bool IsConnected(long playerId)
	{
		if (!PeerByPlayer.TryGetValue(playerId, out var value))
		{
			return false;
		}
		if ((Object)(object)ZNet.instance == (Object)null)
		{
			return false;
		}
		if (value == ZNet.GetUID())
		{
			return true;
		}
		return ZNet.instance.GetPeer(value) != null;
	}

	private static void SendStateToHird(Vault v)
	{
		foreach (HirdMember member in v.Hird.Members)
		{
			SendState(member.PlayerId);
		}
		foreach (HirdInvite invite in v.Hird.Invites)
		{
			SendState(invite.PlayerId);
		}
	}

	public static void BroadcastRoster()
	{
		foreach (long item in new List<long>(NameByPlayer.Keys))
		{
			if (IsConnected(item))
			{
				SendState(item);
			}
		}
	}
}
public enum SortMode
{
	Name,
	Quantity,
	Progression,
	Category
}
public static class KistSort
{
	private struct Key
	{
		public ItemData Item;

		public string Name;

		public int Tier;

		public int Cat;

		public int Total;

		public int Stack;
	}

	public const int UnknownTier = 99;

	private static readonly string[] TierNames = new string[8] { "Meadows", "Black Forest", "Swamp", "Mountain", "Plains", "Mistlands", "Ashlands", "Deep North" };

	private static Dictionary<string, int> _tier;

	private static Dictionary<string, ItemCategory> _catCache;

	private static readonly string[][] Seeds = new string[7][]
	{
		new string[14]
		{
			"Wood", "Stone", "Flint", "Resin", "Feathers", "LeatherScraps", "DeerHide", "Dandelion", "Mushroom", "Raspberry",
			"Honey", "NeckTail", "DeerMeat", "Coal"
		},
		new string[19]
		{
			"RoundLog", "FineWood", "CopperOre", "Copper", "TinOre", "Tin", "Bronze", "SurtlingCore", "GreydwarfEye", "BoneFragments",
			"Thistle", "Blueberries", "Ruby", "TrollHide", "Carrot", "CarrotSeeds", "Acorn", "BeechSeeds", "PineCone"
		},
		new string[16]
		{
			"IronScrap", "Iron", "ElderBark", "Guck", "Chain", "WitheredBone", "Entrails", "Bloodbag", "Ooze", "AncientSeed",
			"Root", "Turnip", "TurnipSeeds", "Amber", "AmberPearl", "Ironpit"
		},
		new string[12]
		{
			"SilverOre", "Silver", "Obsidian", "WolfPelt", "WolfFang", "FreezeGland", "Crystal", "Onion", "OnionSeeds", "JuteRed",
			"DragonEgg", "DragonTear"
		},
		new string[11]
		{
			"BlackMetalScrap", "BlackMetal", "Flax", "Barley", "LinenThread", "BarleyFlour", "Needle", "Tar", "LoxPelt", "Cloudberry",
			"RoyalJelly"
		},
		new string[16]
		{
			"BlackMarble", "YggdrasilWood", "Eitr", "Sap", "Softtissue", "BlackCore", "Wisp", "Carapace", "MechanicalSpring", "ScaleHide",
			"Mandible", "JuteBlue", "Thunderstone", "GiantBloodSack", "MushroomJotunPuffs", "MushroomMagecap"
		},
		new string[15]
		{
			"Flametal", "FlametalNew", "FlametalOreNew", "AskHide", "CharredBone", "MoltenCore", "ProustitePowder", "CelestialFeather", "MorgenSinew", "Grausten",
			"Blackwood", "Sulfur", "Vineberry", "BonemawTooth", "CharcoalResin"
		}
	};

	private static readonly KeyValuePair<Biome, int>[] BiomeOrder = new KeyValuePair<Biome, int>[9]
	{
		new KeyValuePair<Biome, int>((Biome)1, 0),
		new KeyValuePair<Biome, int>((Biome)8, 1),
		new KeyValuePair<Biome, int>((Biome)256, 1),
		new KeyValuePair<Biome, int>((Biome)2, 2),
		new KeyValuePair<Biome, int>((Biome)4, 3),
		new KeyValuePair<Biome, int>((Biome)16, 4),
		new KeyValuePair<Biome, int>((Biome)512, 5),
		new KeyValuePair<Biome, int>((Biome)32, 6),
		new KeyValuePair<Biome, int>((Biome)64, 7)
	};

	public static string ModeLabel(SortMode m)
	{
		return m switch
		{
			SortMode.Quantity => "Amount", 
			SortMode.Progression => "Progress", 
			SortMode.Category => "Type", 
			_ => "A-Z", 
		};
	}

	public static string RowTag(ItemData item, SortMode mode)
	{
		if (item == null)
		{
			return "";
		}
		switch (mode)
		{
		case SortMode.Progression:
		{
			int num = TierOf(item);
			if (num < 0 || num >= TierNames.Length)
			{
				return "unknown";
			}
			return TierNames[num];
		}
		case SortMode.Category:
			return KistCategories.NameOf(CategoryOf(item));
		default:
			return "";
		}
	}

	public static List<ItemData> Arrange(List<ItemData> items, SortMode mode)
	{
		List<ItemData> list = new List<ItemData>();
		if (items == null)
		{
			return list;
		}
		Dictionary<string, int> dictionary = null;
		if (mode == SortMode.Quantity)
		{
			dictionary = new Dictionary<string, int>();
			foreach (ItemData item2 in items)
			{
				string text = TokenOf(item2);
				if (text != null)
				{
					dictionary[text] = (dictionary.TryGetValue(text, out var value) ? (value + item2.m_stack) : item2.m_stack);
				}
			}
		}
		List<Key> list2 = new List<Key>(items.Count);
		foreach (ItemData item3 in items)
		{
			Key item = new Key
			{
				Item = item3,
				Name = DisplayName(item3),
				Stack = item3.m_stack,
				Tier = ((mode == SortMode.Progression || mode == SortMode.Category) ? TierOf(item3) : 0),
				Cat = (int)((mode == SortMode.Progression || mode == SortMode.Category) ? CategoryOf(item3) : ItemCategory.Weapon)
			};
			if (dictionary != null)
			{
				string text2 = TokenOf(item3);
				item.Total = ((text2 != null && dictionary.TryGetValue(text2, out var value2)) ? value2 : item3.m_stack);
			}
			list2.Add(item);
		}
		list2.Sort(delegate(Key a, Key b)
		{
			switch (mode)
			{
			case SortMode.Quantity:
			{
				int num = b.Total.CompareTo(a.Total);
				if (num != 0)
				{
					return num;
				}
				num = ByName(a, b);
				if (num != 0)
				{
					return num;
				}
				return b.Stack.CompareTo(a.Stack);
			}
			case SortMode.Progression:
			{
				int num = a.Tier.CompareTo(b.Tier);
				if (num != 0)
				{
					return num;
				}
				num = a.Cat.CompareTo(b.Cat);
				if (num != 0)
				{
					return num;
				}
				return ByName(a, b);
			}
			case SortMode.Category:
			{
				int num = a.Cat.CompareTo(b.Cat);
				if (num != 0)
				{
					return num;
				}
				num = a.Tier.CompareTo(b.Tier);
				if (num != 0)
				{
					return num;
				}
				return ByName(a, b);
			}
			default:
				return ByName(a, b);
			}
		});
		foreach (Key item4 in list2)
		{
			list.Add(item4.Item);
		}
		return list;
	}

	private static int ByName(Key a, Key b)
	{
		int num = string.Compare(a.Name, b.Name, StringComparison.CurrentCultureIgnoreCase);
		if (num != 0)
		{
			return num;
		}
		int num2 = ((a.Item != null) ? a.Item.m_quality : 0);
		int value = ((b.Item != null) ? b.Item.m_quality : 0);
		return num2.CompareTo(value);
	}

	public static string DisplayName(ItemData item)
	{
		if (item == null || item.m_shared == null)
		{
			return "";
		}
		if (Localization.instance == null)
		{
			return item.m_shared.m_name;
		}
		return Localization.instance.Localize(item.m_shared.m_name);
	}

	private static string TokenOf(ItemData item)
	{
		if (item == null || item.m_shared == null)
		{
			return null;
		}
		return item.m_shared.m_name;
	}

	public static ItemCategory CategoryOf(ItemData item)
	{
		if (item == null || item.m_shared == null)
		{
			return ItemCategory.Other;
		}
		string name = item.m_shared.m_name;
		if (string.IsNullOrEmpty(name))
		{
			return ItemCategory.Other;
		}
		if (_catCache == null)
		{
			_catCache = new Dictionary<string, ItemCategory>();
		}
		if (_catCache.TryGetValue(name, out var value))
		{
			return value;
		}
		ItemCategory itemCategory = Classify(item, name);
		_catCache[name] = itemCategory;
		return itemCategory;
	}

	private static ItemCategory Classify(ItemData item, string token)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Expected I4, but got Unknown
		if (KistCategories.FromItemType((int)item.m_shared.m_itemType, out var category))
		{
			return category;
		}
		return KistCategories.FromToken(token);
	}

	public static int TierOf(ItemData item)
	{
		string text = TokenOf(item);
		if (string.IsNullOrEmpty(text))
		{
			return 99;
		}
		EnsureTiers();
		if (_tier == null || !_tier.TryGetValue(text, out var value))
		{
			return 99;
		}
		return value;
	}

	public static void Reset()
	{
		_tier = null;
		_catCache = null;
	}

	private static void EnsureTiers()
	{
		if (_tier == null && !((Object)(object)ObjectDB.instance == (Object)null))
		{
			_tier = new Dictionary<string, int>();
			int num = Guarded("seed", SeedPass);
			int num2 = Guarded("vegetation", VegetationPass);
			int num3 = Guarded("creatures", CreaturePass);
			int num4 = Guarded("recipes", RecipePass);
			Main.Log.LogInfo((object)("ThorsKist: progression table built -- " + _tier.Count + " items (" + num + " seeded, " + num2 + " from world growth, " + num3 + " from creature drops, " + num4 + " from recipes)."));
		}
	}

	private static int Guarded(string what, Func<int> pass)
	{
		try
		{
			return pass();
		}
		catch (Exception ex)
		{
			Main.Log.LogWarning((object)("ThorsKist: progression pass '" + what + "' failed, items it would have covered will sort as unknown. " + ex.Message));
			return 0;
		}
	}

	private static bool Note(string token, int tier)
	{
		if (string.IsNullOrEmpty(token) || tier < 0 || tier >= TierNames.Length)
		{
			return false;
		}
		if (_tier.TryGetValue(token, out var value))
		{
			if (tier >= value)
			{
				return false;
			}
			_tier[token] = tier;
			return false;
		}
		_tier[token] = tier;
		return true;
	}

	private static int SeedPass()
	{
		ObjectDB instance = ObjectDB.instance;
		if ((Object)(object)instance == (Object)null)
		{
			return 0;
		}
		int num = 0;
		List<string> list = null;
		for (int i = 0; i < Seeds.Length; i++)
		{
			string[] array = Seeds[i];
			foreach (string text in array)
			{
				string text2 = TokenOf(instance.GetItemPrefab(text));
				if (text2 == null)
				{
					if (list == null)
					{
						list = new List<string>();
					}
					list.Add(text);
				}
				else if (Note(text2, i))
				{
					num++;
				}
			}
		}
		if (list != null)
		{
			Main.Log.LogInfo((object)("ThorsKist: " + list.Count + " seeded prefab names no longer resolve (" + string.Join(", ", list.ToArray()) + "). They will sort as unknown."));
		}
		return num;
	}

	private static int VegetationPass()
	{
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		ZoneSystem instance = ZoneSystem.instance;
		if ((Object)(object)instance == (Object)null || instance.m_vegetation == null)
		{
			return 0;
		}
		int num = 0;
		foreach (ZoneVegetation item in instance.m_vegetation)
		{
			if (item != null && !((Object)(object)item.m_prefab == (Object)null))
			{
				int num2 = BiomeTier(item.m_biome);
				if (num2 >= 0)
				{
					num += HarvestFrom(item.m_prefab, num2);
				}
			}
		}
		return num;
	}

	private static int HarvestFrom(GameObject prefab, int tier)
	{
		int num = 0;
		Pickable[] componentsInChildren = prefab.GetComponentsInChildren<Pickable>(true);
		foreach (Pickable val in componentsInChildren)
		{
			if ((Object)(object)val != (Object)null && Note(TokenOf(val.m_itemPrefab), tier))
			{
				num++;
			}
		}
		DropOnDestroyed[] componentsInChildren2 = prefab.GetComponentsInChildren<DropOnDestroyed>(true);
		foreach (DropOnDestroyed val2 in componentsInChildren2)
		{
			if ((Object)(object)val2 != (Object)null)
			{
				num += FromTable(val2.m_dropWhenDestroyed, tier);
			}
		}
		MineRock[] componentsInChildren3 = prefab.GetComponentsInChildren<MineRock>(true);
		foreach (MineRock val3 in componentsInChildren3)
		{
			if ((Object)(object)val3 != (Object)null)
			{
				num += FromTable(val3.m_dropItems, tier);
			}
		}
		MineRock5[] componentsInChildren4 = prefab.GetComponentsInChildren<MineRock5>(true);
		foreach (MineRock5 val4 in componentsInChildren4)
		{
			if ((Object)(object)val4 != (Object)null)
			{
				num += FromTable(val4.m_dropItems, tier);
			}
		}
		return num;
	}

	private static int FromTable(DropTable table, int tier)
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		if (table == null || table.m_drops == null)
		{
			return 0;
		}
		int num = 0;
		foreach (DropData drop in table.m_drops)
		{
			if (Note(TokenOf(drop.m_item), tier))
			{
				num++;
			}
		}
		return num;
	}

	private static int CreaturePass()
	{
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		SpawnSystem[] array = Object.FindObjectsOfType<SpawnSystem>(true);
		if (array == null)
		{
			return 0;
		}
		List<SpawnSystemList> list = new List<SpawnSystemList>();
		SpawnSystem[] array2 = array;
		foreach (SpawnSystem val in array2)
		{
			if ((Object)(object)val == (Object)null || val.m_spawnLists == null)
			{
				continue;
			}
			foreach (SpawnSystemList spawnList in val.m_spawnLists)
			{
				if ((Object)(object)spawnList != (Object)null && !list.Contains(spawnList))
				{
					list.Add(spawnList);
				}
			}
		}
		int num = 0;
		foreach (SpawnSystemList item in list)
		{
			if ((Object)(object)item == (Object)null || item.m_spawners == null)
			{
				continue;
			}
			foreach (SpawnData spawner in item.m_spawners)
			{
				if (spawner == null || (Object)(object)spawner.m_prefab == (Object)null)
				{
					continue;
				}
				int num2 = BiomeTier(spawner.m_biome);
				if (num2 < 0)
				{
					continue;
				}
				CharacterDrop[] componentsInChildren = spawner.m_prefab.GetComponentsInChildren<CharacterDrop>(true);
				foreach (CharacterDrop val2 in componentsInChildren)
				{
					if ((Object)(object)val2 == (Object)null || val2.m_drops == null)
					{
						continue;
					}
					foreach (Drop drop in val2.m_drops)
					{
						if (Note(TokenOf(drop.m_prefab), num2))
						{
							num++;
						}
					}
				}
			}
		}
		return num;
	}

	private static int RecipePass()
	{
		ObjectDB instance = ObjectDB.instance;
		if ((Object)(object)instance == (Object)null || instance.m_recipes == null)
		{
			return 0;
		}
		int num = 0;
		for (int i = 0; i < 8; i++)
		{
			bool flag = false;
			foreach (Recipe recipe in instance.m_recipes)
			{
				if ((Object)(object)recipe == (Object)null || !recipe.m_enabled || (Object)(object)recipe.m_item == (Object)null)
				{
					continue;
				}
				string text = TokenOf(((Component)recipe.m_item).gameObject);
				if (text == null)
				{
					continue;
				}
				int num2 = StationTier(recipe.m_craftingStation);
				bool flag2 = num2 >= 0;
				if (recipe.m_resources != null)
				{
					Requirement[] resources = recipe.m_resources;
					foreach (Requirement val in resources)
					{
						if (val == null || (Object)(object)val.m_resItem == (Object)null)
						{
							continue;
						}
						string text2 = TokenOf(((Component)val.m_resItem).gameObject);
						if (text2 != null && _tier.TryGetValue(text2, out var value))
						{
							if (value > num2)
							{
								num2 = value;
							}
							flag2 = true;
						}
					}
				}
				if (!flag2 || num2 < 0)
				{
					continue;
				}
				if (_tier.TryGetValue(text, out var value2))
				{
					if (num2 >= value2)
					{
						continue;
					}
				}
				else
				{
					num++;
				}
				_tier[text] = num2;
				flag = true;
			}
			if (!flag)
			{
				break;
			}
		}
		return num;
	}

	private static int StationTier(CraftingStation station)
	{
		if ((Object)(object)station == (Object)null)
		{
			return -1;
		}
		string text = ((((Object)station).name != null) ? ((Object)station).name.ToLowerInvariant() : "");
		if (text.Contains("blackforge"))
		{
			return 5;
		}
		if (text.Contains("magetable") || text.Contains("galdr"))
		{
			return 5;
		}
		if (text.Contains("artisan"))
		{
			return 4;
		}
		if (text.Contains("forge"))
		{
			return 1;
		}
		if (text.Contains("stonecutter"))
		{
			return 2;
		}
		if (text.Contains("workbench") || text.Contains("cauldron") || text.Contains("preptable"))
		{
			return 0;
		}
		return -1;
	}

	private static string TokenOf(GameObject go)
	{
		if ((Object)(object)go == (Object)null)
		{
			return null;
		}
		ItemDrop component = go.GetComponent<ItemDrop>();
		if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null)
		{
			return null;
		}
		return component.m_itemData.m_shared.m_name;
	}

	private static int BiomeTier(Biome biome)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		int num = -1;
		KeyValuePair<Biome, int>[] biomeOrder = BiomeOrder;
		for (int i = 0; i < biomeOrder.Length; i++)
		{
			KeyValuePair<Biome, int> keyValuePair = biomeOrder[i];
			if ((biome & keyValuePair.Key) != 0 && (num < 0 || keyValuePair.Value < num))
			{
				num = keyValuePair.Value;
			}
		}
		return num;
	}
}
public static class StationScope
{
	private static int _depth;

	private static int _commit;

	public static string Label = "";

	public static bool Active
	{
		get
		{
			if (_depth > 0)
			{
				return CraftContext.Active;
			}
			return false;
		}
	}

	public static bool Committing
	{
		get
		{
			if (_commit > 0)
			{
				return Active;
			}
			return false;
		}
	}

	public static string State => "depth=" + _depth + " commit=" + _commit + " craftActive=" + CraftContext.Active + " canCraft=" + KistClient.CanCraft;

	public static void Enter(bool committing, string label = "?")
	{
		if (_depth == 0)
		{
			Label = label;
		}
		else
		{
			Label = Label + ">" + label;
		}
		_depth++;
		if (committing)
		{
			_commit++;
		}
		CraftContext.Push();
	}

	public static void Exit(bool committing)
	{
		CraftContext.Pop();
		if (committing && _commit > 0)
		{
			_commit--;
		}
		if (_depth > 0)
		{
			_depth--;
		}
		if (_depth == 0)
		{
			CraftContext.FlushDraws();
		}
	}

	public static void FrameReset()
	{
		if (_depth != 0)
		{
			Main.Log.LogWarning((object)("ThorsKist: station scope was left open (depth " + _depth + ") -- a station call threw. Forcing it shut."));
			while (_depth > 0)
			{
				CraftContext.Pop();
				_depth--;
			}
			_commit = 0;
			CraftContext.FlushDraws();
		}
	}
}
[HarmonyPatch]
public static class Patches_StationScope
{
	[HarmonyPatch(typeof(Smelter), "OnAddOre")]
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	public static void SmelterOrePre()
	{
		StationScope.Enter(committing: true, "Smelter.OnAddOre");
	}

	[HarmonyPatch(typeof(Smelter), "OnAddOre")]
	[HarmonyPostfix]
	public static void SmelterOrePost()
	{
		StationScope.Exit(committing: true);
	}

	[HarmonyPatch(typeof(Smelter), "OnAddFuel")]
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	public static void SmelterFuelPre()
	{
		StationScope.Enter(committing: true, "Smelter.OnAddFuel");
	}

	[HarmonyPatch(typeof(Smelter), "OnAddFuel")]
	[HarmonyPostfix]
	public static void SmelterFuelPost()
	{
		StationScope.Exit(committing: true);
	}

	[HarmonyPatch(typeof(Smelter), "CanUseItems")]
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	public static void SmelterCanPre()
	{
		StationScope.Enter(committing: false, "Smelter.CanUseItems");
	}

	[HarmonyPatch(typeof(Smelter), "CanUseItems")]
	[HarmonyPostfix]
	public static void SmelterCanPost()
	{
		StationScope.Exit(committing: false);
	}

	[HarmonyPatch(typeof(Fireplace), "Interact")]
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	public static void FireplaceInteractPre()
	{
		StationScope.Enter(committing: true, "Fireplace.Interact");
	}

	[HarmonyPatch(typeof(Fireplace), "Interact")]
	[HarmonyPostfix]
	public static void FireplaceInteractPost()
	{
		StationScope.Exit(committing: true);
	}

	[HarmonyPatch(typeof(Fireplace), "UseItem")]
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	public static void FireplaceUsePre()
	{
		StationScope.Enter(committing: true, "Fireplace.UseItem");
	}

	[HarmonyPatch(typeof(Fireplace), "UseItem")]
	[HarmonyPostfix]
	public static void FireplaceUsePost()
	{
		StationScope.Exit(committing: true);
	}

	[HarmonyPatch(typeof(Fireplace), "CanUseItems")]
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	public static void FireplaceCanPre()
	{
		StationScope.Enter(committing: false, "Fireplace.CanUseItems");
	}

	[HarmonyPatch(typeof(Fireplace), "CanUseItems")]
	[HarmonyPostfix]
	public static void FireplaceCanPost()
	{
		StationScope.Exit(committing: false);
	}

	[HarmonyPatch(typeof(CookingStation), "OnAddFuelSwitch")]
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	public static void CookFuelPre()
	{
		StationScope.Enter(committing: true, "CookingStation.OnAddFuelSwitch");
	}

	[HarmonyPatch(typeof(CookingStation), "OnAddFuelSwitch")]
	[HarmonyPostfix]
	public static void CookFuelPost()
	{
		StationScope.Exit(committing: true);
	}

	[HarmonyPatch(typeof(CookingStation), "OnAddFoodSwitch")]
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	public static void CookFoodPre()
	{
		StationScope.Enter(committing: true, "CookingStation.OnAddFoodSwitch");
	}

	[HarmonyPatch(typeof(CookingStation), "OnAddFoodSwitch")]
	[HarmonyPostfix]
	public static void CookFoodPost()
	{
		StationScope.Exit(committing: true);
	}

	[HarmonyPatch(typeof(CookingStation), "UseItem")]
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	public static void CookUsePre()
	{
		StationScope.Enter(committing: true, "CookingStation.UseItem");
	}

	[HarmonyPatch(typeof(CookingStation), "UseItem")]
	[HarmonyPostfix]
	public static void CookUsePost()
	{
		StationScope.Exit(committing: true);
	}

	[HarmonyPatch(typeof(CookingStation), "CanUseItems")]
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	public static void CookCanPre()
	{
		StationScope.Enter(committing: false, "CookingStation.CanUseItems");
	}

	[HarmonyPatch(typeof(CookingStation), "CanUseItems")]
	[HarmonyPostfix]
	public static void CookCanPost()
	{
		StationScope.Exit(committing: false);
	}

	[HarmonyPatch(typeof(CookingStation), "OnInteract")]
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	public static void CookInteractPre()
	{
		StationScope.Enter(committing: true, "CookingStation.OnInteract");
	}

	[HarmonyPatch(typeof(CookingStation), "OnInteract")]
	[HarmonyPostfix]
	public static void CookInteractPost()
	{
		StationScope.Exit(committing: true);
	}

	[HarmonyPatch(typeof(Fermenter), "Interact")]
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	public static void FermentInteractPre()
	{
		StationScope.Enter(committing: true, "Fermenter.Interact");
	}

	[HarmonyPatch(typeof(Fermenter), "Interact")]
	[HarmonyPostfix]
	public static void FermentInteractPost()
	{
		StationScope.Exit(committing: true);
	}

	[HarmonyPatch(typeof(Fermenter), "AddItem")]
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	public static void FermentAddPre()
	{
		StationScope.Enter(committing: true, "Fermenter.AddItem");
	}

	[HarmonyPatch(typeof(Fermenter), "AddItem")]
	[HarmonyPostfix]
	public static void FermentAddPost()
	{
		StationScope.Exit(committing: true);
	}

	[HarmonyPatch(typeof(Fermenter), "UseItem")]
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	public static void FermentUsePre()
	{
		StationScope.Enter(committing: true, "Fermenter.UseItem");
	}

	[HarmonyPatch(typeof(Fermenter), "UseItem")]
	[HarmonyPostfix]
	public static void FermentUsePost()
	{
		StationScope.Exit(committing: true);
	}
}
[HarmonyPatch(typeof(Inventory), "HaveItem", new Type[]
{
	typeof(string),
	typeof(bool)
})]
public static class Patches_HaveItem
{
	[HarmonyPostfix]
	public static void Postfix(Inventory __instance, string name, ref bool __result)
	{
		if (__result || !StationScope.Active || !CraftContext.IsPlayerInventory(__instance))
		{
			return;
		}
		Inventory kist = KistClient.Kist;
		if (kist == null)
		{
			return;
		}
		CraftContext.EnterRaw();
		try
		{
			if (KistStock.Count(kist, name, -1) > 0)
			{
				__result = true;
			}
		}
		finally
		{
			CraftContext.ExitRaw();
		}
	}
}
[HarmonyPatch(typeof(Inventory), "CountItemsByName")]
public static class Patches_CountItemsByName
{
	[HarmonyPostfix]
	public static void Postfix(Inventory __instance, string[] names, int quality, ref int __result)
	{
		if (!StationScope.Active || !CraftContext.IsPlayerInventory(__instance) || names == null)
		{
			return;
		}
		Inventory kist = KistClient.Kist;
		if (kist == null)
		{
			return;
		}
		CraftContext.EnterRaw();
		try
		{
			foreach (string text in names)
			{
				if (!string.IsNullOrEmpty(text))
				{
					__result += KistStock.Count(kist, text, quality);
				}
			}
		}
		finally
		{
			CraftContext.ExitRaw();
		}
	}
}
[HarmonyPatch(typeof(Inventory), "GetItem", new Type[]
{
	typeof(string),
	typeof(int),
	typeof(bool)
})]
public static class Patches_GetItem
{
	[HarmonyPostfix]
	public static void Postfix(Inventory __instance, string name, int quality, bool isPrefabName, ref ItemData __result)
	{
		if (__result != null || !StationScope.Active || isPrefabName || !CraftContext.IsPlayerInventory(__instance))
		{
			return;
		}
		Inventory kist = KistClient.Kist;
		if (kist == null)
		{
			return;
		}
		bool committing = StationScope.Committing;
		string state = StationScope.State;
		CraftContext.EnterRaw();
		try
		{
			ItemData val = KistStock.Find(kist, name, quality);
			if (val == null)
			{
				if (committing)
				{
					KistClient.ReportDiag("station[" + StationScope.Label + "] wanted " + name + " and the Kist holds none that match. " + state);
				}
				return;
			}
			if (!committing)
			{
				ItemData val2 = val.Clone();
				val2.m_stack = 1;
				__result = val2;
				return;
			}
			ItemData val3 = val.Clone();
			val3.m_stack = 1;
			if (!__instance.CanAddItem(val3, 1))
			{
				KistClient.Message("$msg_yourinventoryisfull");
				KistClient.ReportDiag("station[" + StationScope.Label + "] could not take " + name + " -- the player's inventory is full.");
			}
			else if (!__instance.AddItem(val3))
			{
				KistClient.ReportDiag("station[" + StationScope.Label + "] could not take " + name + " -- AddItem refused it.");
			}
			else
			{
				KistStock.Take(kist, name, 1, quality);
				CraftContext.Record(name, 1, quality);
				Main.Log.LogInfo((object)("ThorsKist: station[" + StationScope.Label + "] took 1 x " + name + " from the Kist."));
				__result = __instance.GetItem(name, quality, false);
			}
		}
		finally
		{
			CraftContext.ExitRaw();
		}
	}
}
internal static class KistStock
{
	public static int Count(Inventory kist, string name, int quality)
	{
		if (kist == null || string.IsNullOrEmpty(name))
		{
			return 0;
		}
		return kist.CountItems(name, quality, false);
	}

	public static void Take(Inventory kist, string name, int amount, int quality)
	{
		if (kist != null && amount > 0)
		{
			kist.RemoveItem(name, amount, quality, false);
		}
	}

	public static ItemData Find(Inventory kist, string name, int quality)
	{
		if (kist == null || string.IsNullOrEmpty(name))
		{
			return null;
		}
		foreach (ItemData allItem in kist.GetAllItems())
		{
			if (allItem != null && allItem.m_shared != null && !(allItem.m_shared.m_name != name) && (quality < 0 || allItem.m_quality == quality))
			{
				return allItem;
			}
		}
		return null;
	}
}
public static class KistUI
{
	private enum Tab
	{
		Kist,
		Hird
	}

	[Serializable]
	[CompilerGenerated]
	private sealed class <>c
	{
		public static readonly <>c <>9 = new <>c();

		public static UnityAction <>9__71_0;

		public static UnityAction <>9__71_1;

		public static UnityAction<string> <>9__71_2;

		public static UnityAction<string> <>9__79_0;

		public static UnityAction <>9__79_1;

		public static UnityAction<string> <>9__80_0;

		public static UnityAction <>9__80_1;

		internal void <Build>b__71_0()
		{
			_tab = Tab.Kist;
			_dirty = true;
		}

		internal void <Build>b__71_1()
		{
			_tab = Tab.Hird;
			_dirty = true;
		}

		internal void <Build>b__71_2(string <p0>)
		{
			_dirty = true;
		}

		internal void <BuildHirdRows>b__79_0(string v)
		{
			_hirdNameDraft = v;
		}

		internal void <BuildHirdRows>b__79_1()
		{
			KistClient.CreateHird(_hirdNameDraft);
			_hirdNameDraft = "";
		}

		internal void <AddRenameRow>b__80_0(string v)
		{
			_renameDraft = v;
		}

		internal void <AddRenameRow>b__80_1()
		{
			KistClient.RenameHird(_renameDraft);
		}
	}

	public static bool IsOpen;

	private static GameObject _root;

	private static RectTransform _content;

	private static RectTransform _hirdContent;

	private static ScrollRect _kistScroll;

	private static ScrollRect _hirdScroll;

	private static GameObject _kistPage;

	private static GameObject _hirdPage;

	private static TMP_Text _title;

	private static TMP_Text _status;

	private static TMP_Text _countLabel;

	private static TMP_InputField _search;

	private static TMP_InputField _hirdName;

	private static string _hirdNameDraft = "";

	private static TMP_InputField _renameField;

	private static string _renameDraft = "";

	private static string _renameSeededFrom;

	private static Button _tabKist;

	private static Button _tabHird;

	private static GameObject _sortBar;

	private static readonly List<Button> _sortButtons = new List<Button>();

	private static readonly SortMode[] SortOrder = new SortMode[4]
	{
		SortMode.Name,
		SortMode.Quantity,
		SortMode.Progression,
		SortMode.Category
	};

	private static TMP_FontAsset _font;

	private static Tab _tab = Tab.Kist;

	private static bool _dirty = true;

	private static bool _lastCanTake;

	private static Vector2 _builtForScreen;

	private static readonly List<GameObject> _rows = new List<GameObject>();

	private const float RowGap = 0f;

	private static readonly Color Parchment = new Color(0.87f, 0.82f, 0.7f, 1f);

	private static readonly Color Dim = new Color(0.62f, 0.58f, 0.5f, 1f);

	private static readonly Color PanelBg = new Color(0.07f, 0.065f, 0.06f, 0.97f);

	private static readonly Color RowBg = new Color(0.13f, 0.12f, 0.1f, 0.85f);

	private static readonly Color RowAlt = new Color(0.17f, 0.15f, 0.12f, 0.85f);

	private static readonly Color Accent = new Color(0.79f, 0.63f, 0.31f, 1f);

	private static readonly Color BtnBg = new Color(0.24f, 0.21f, 0.16f, 1f);

	private static readonly Color BtnBgHot = new Color(0.36f, 0.3f, 0.2f, 1f);

	private static readonly Color Bad = new Color(0.82f, 0.36f, 0.3f, 1f);

	private static bool _sortReversed;

	public static bool BlocksGameInput
	{
		get
		{
			if (IsOpen && (Object)(object)_root != (Object)null)
			{
				return _root.activeInHierarchy;
			}
			return false;
		}
	}

	public static bool WantsAnchorStatus
	{
		get
		{
			if (IsOpen && _tab == Tab.Hird)
			{
				return KistClient.IsOwner;
			}
			return false;
		}
	}

	private static float S => Mathf.Clamp(Main.UiScale.Value, 0.6f, 1.6f);

	private static float RowH => Mathf.Round(18f * S);

	private static float FontRow => 12f * S;

	private static float FontSmall => 10.5f * S;

	private static float FontHead => 20f * S;

	private static float I