Decompiled source of Innangard v0.3.5

plugins/Innangard.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Innangard.Blueprints;
using Innangard.Jobs;
using Innangard.Trade;
using Innangard.UI;
using Jotunn;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Splatform;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: AssemblyCompany("Innangard")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.3.5.0")]
[assembly: AssemblyInformationalVersion("0.3.5")]
[assembly: AssemblyProduct("Innangard")]
[assembly: AssemblyTitle("Innangard")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.3.5.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Innangard
{
	[HarmonyPatch(typeof(WearNTear), "Destroy")]
	internal static class BaseDestroyedPatch
	{
		[HarmonyPrefix]
		private static void Prefix(WearNTear __instance, HitData hitData)
		{
			if (hitData != null)
			{
				SettlementAlarm.StructureLost(__instance, hitData.GetAttacker());
			}
			Frithstone frithstone = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).GetComponentInParent<Frithstone>() : null);
			if ((Object)(object)frithstone != (Object)null && (Object)(object)frithstone.Net != (Object)null && frithstone.Net.IsValid())
			{
				SettlementCode.RememberVacated(frithstone.Net.GetZDO());
			}
		}
	}
	[HarmonyPatch]
	internal static class BuildMenuCategory
	{
		internal const UsageTagFlags Ours = (UsageTagFlags)1073741824;

		private const string Label = "Innangard";

		internal static void Claim(GameObject prefab)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			Piece val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<Piece>() : null);
			if ((Object)(object)val != (Object)null)
			{
				val.m_usage = (UsageTagFlags)1073741824;
			}
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		private static void PutOurRowFirst(ByUsagePieceList __instance)
		{
			UsageTagFlags[] usageTags = __instance.m_usageTags;
			string[] usageTagDisplayNames = __instance.m_usageTagDisplayNames;
			if (usageTags == null || usageTagDisplayNames == null || usageTags.Length != usageTagDisplayNames.Length)
			{
				Logger.LogWarning((object)"Innangard: the build menu's tag list is not the shape this was written for, so the mod's pieces stay under Misc.");
			}
			else if (usageTags.Length == 0 || (int)usageTags[0] != 1073741824)
			{
				UsageTagFlags[] array = (UsageTagFlags[])(object)new UsageTagFlags[usageTags.Length + 1];
				string[] array2 = new string[usageTagDisplayNames.Length + 1];
				array[0] = (UsageTagFlags)1073741824;
				array2[0] = "Innangard";
				Array.Copy(usageTags, 0, array, 1, usageTags.Length);
				Array.Copy(usageTagDisplayNames, 0, array2, 1, usageTagDisplayNames.Length);
				Set("m_usageTags", __instance, array);
				Set("m_usageTagDisplayNames", __instance, array2);
			}
		}

		private static void Set(string field, ByUsagePieceList list, object value)
		{
			FieldInfo fieldInfo = AccessTools.Field(typeof(ByUsagePieceList), field);
			if (fieldInfo == null)
			{
				Logger.LogWarning((object)("Innangard: the build menu has no " + field + " to add a row to; the mod's pieces stay under Misc."));
			}
			else
			{
				fieldInfo.SetValue(list, value);
			}
		}
	}
	[Flags]
	internal enum CaretakerDuties
	{
		None = 0,
		Mending = 1,
		Sorting = 2,
		Fires = 4,
		MachineFuel = 8,
		MachineInput = 0x10,
		Gathering = 0x20,
		All = 0x3F
	}
	internal sealed class CaretakerTestCommand : ConsoleCommand
	{
		private static bool _running;

		private static readonly string[] ItemPrefabs = new string[34]
		{
			"Flint", "Amber", "Ruby", "AmberPearl", "Chitin", "Obsidian", "Crystal", "Ectoplasm", "WitheredBone", "Bloodbag",
			"Ooze", "Thistle", "Guck", "FreezeGland", "Carapace", "Sap", "Needle", "Feathers", "GreydwarfEye", "Entrails",
			"Resin", "Bilebag", "Mandible", "RoyalJelly", "LinenThread", "Barley", "Flax", "SharpeningStone", "Tar", "BlackCore",
			"Eitr", "Softtissue", "Wisp", "DragonTear"
		};

		private const string ChestPrefab = "piece_chest_wood";

		private static bool _stop;

		private static readonly int Floors = LayerMask.GetMask(new string[3] { "static_solid", "terrain", "piece" });

		private static readonly int Solids = LayerMask.GetMask(new string[6] { "Default", "static_solid", "piece", "piece_nonsolid", "character", "terrain" });

		public override string Name => "inn.test.caretaker";

		public override string Help => "[seconds] - (devcommands) puts three chests and something to sort beside you, watches the caretakers sort it, reports, then cleans up";

		public override bool IsCheat => true;

		public override void Run(string[] args)
		{
			if (!DebugCommand.Allowed(((ConsoleCommand)this).Name))
			{
				return;
			}
			if (args.Length != 0 && args[0] == "stop")
			{
				_stop = _running;
				Console.instance.Print(_running ? "Innangard: stopping the caretaker test and cleaning up." : "Innangard: the caretaker test is not running.");
				return;
			}
			if (_running)
			{
				Console.instance.Print("Innangard: the caretaker test is already running. \"inn.test.caretaker stop\" ends it.");
				return;
			}
			_stop = false;
			float seconds = 180f;
			if (args.Length != 0 && float.TryParse(args[0], out var result) && result >= 30f)
			{
				seconds = result;
			}
			InnangardPlugin instance = InnangardPlugin.Instance;
			if (instance != null)
			{
				((MonoBehaviour)instance).StartCoroutine(Watch(seconds));
			}
		}

		private static void Say(string text)
		{
			Console instance = Console.instance;
			if (instance != null)
			{
				instance.Print(text);
			}
			Logger.LogInfo((object)("Innangard[caretaker-test]: " + text));
		}

		private static int CountIn(Container chest, string name)
		{
			Inventory val = (((Object)(object)chest != (Object)null) ? chest.GetInventory() : null);
			if (val != null)
			{
				return (from item in val.GetAllItems()
					where item?.m_shared?.m_name == name
					select item).Sum((ItemData item) => item.m_stack);
			}
			return 0;
		}

		private static IEnumerator Watch(float seconds)
		{
			_running = true;
			List<GameObject> made = new List<GameObject>();
			List<ItemDrop> loose = new List<ItemDrop>();
			Container home = null;
			Container strayA = null;
			Container strayB = null;
			string name = null;
			int spawned = 0;
			try
			{
				Player player = Player.m_localPlayer;
				SettlerController asking = (((Object)(object)player != (Object)null) ? ChestLabels.ResidentHere(((Component)player).transform.position) : null);
				if ((Object)(object)asking == (Object)null)
				{
					Say("Stand inside one of your settlements and run it again.");
					yield break;
				}
				List<SettlerController> caretakers = SettlerController.Instances.Where((SettlerController s) => (Object)(object)s != (Object)null && s.IsValidSettler && s.CurrentRole == SettlerRole.Caretaker && s.SettlementOwnerId == asking.SettlementOwnerId && s.InSettlementArea(((Component)player).transform.position)).ToList();
				if (caretakers.Count == 0)
				{
					Say("There is no caretaker in this settlement. Give someone the trade and run it again.");
					yield break;
				}
				ZNetScene instance = ZNetScene.instance;
				GameObject chestPrefab = ((instance != null) ? instance.GetPrefab("piece_chest_wood") : null);
				List<Container> source = SettlementStorage.Managed(asking);
				GameObject itemPrefab = null;
				int fewest = int.MaxValue;
				string[] itemPrefabs = ItemPrefabs;
				foreach (string text in itemPrefabs)
				{
					ObjectDB instance2 = ObjectDB.instance;
					GameObject val = ((instance2 != null) ? instance2.GetItemPrefab(text) : null);
					ItemData val2 = ((val == null) ? null : val.GetComponent<ItemDrop>()?.m_itemData);
					if (val2 != null && ChestSortRules.IsSortable(val2))
					{
						string held = val2.m_shared.m_name;
						int num2 = source.Sum((Container chest) => CountIn(chest, held)) + caretakers.Sum((SettlerController c) => c.InventoryItemCount(held));
						if ((Object)(object)itemPrefab == (Object)null || num2 < fewest)
						{
							itemPrefab = val;
							fewest = num2;
						}
						if (num2 == 0)
						{
							break;
						}
					}
				}
				GameObject obj = itemPrefab;
				ItemData sample = ((obj == null) ? null : obj.GetComponent<ItemDrop>()?.m_itemData);
				if ((Object)(object)chestPrefab == (Object)null || sample == null)
				{
					Say(((Object)(object)chestPrefab == (Object)null) ? "Could not find piece_chest_wood in the game." : "None of the materials the test sorts could be found in the game.");
					yield break;
				}
				name = sample.m_shared.m_name;
				Vector3 at = ((Component)player).transform.position;
				Vector3 val3 = Vector3.ProjectOnPlane(((Component)player).transform.forward, Vector3.up);
				Vector3 ahead = ((Vector3)(ref val3)).normalized;
				home = Place(0f, "home");
				strayA = Place(55f, "A");
				strayB = Place(-55f, "B");
				yield return null;
				yield return null;
				ChestSortRules.RequestLabel(home, "Test: " + ChestLabels.Pretty(name) + " goes here");
				ChestSortRules.RequestLabel(strayA, "Test: strays A");
				ChestSortRules.RequestLabel(strayB, "Test: strays B");
				ChestSortRules.RequestSet(home, new string[1] { name }, configured: true);
				ChestSortRules.RequestSet(strayA, new string[0], configured: true);
				ChestSortRules.RequestSet(strayB, new string[0], configured: true);
				Fill(strayA, 20);
				Fill(strayA, 20);
				Fill(strayB, 20);
				spawned = CountIn(strayA, name) + CountIn(strayB, name);
				for (int num3 = 0; num3 < 3; num3++)
				{
					ItemData obj2 = sample.Clone();
					obj2.m_stack = 5;
					obj2.m_dropPrefab = itemPrefab;
					Vector3 val4 = at - ahead * 3f + Quaternion.Euler(0f, 90f, 0f) * ahead * (float)(num3 - 1) * 1.5f;
					val4.y = FloorAt(val4, at.y) + 0.3f;
					ItemDrop val5 = ItemDrop.DropItem(obj2, 5, val4, Quaternion.identity);
					if ((Object)(object)val5 != (Object)null)
					{
						loose.Add(val5);
						spawned += 5;
					}
				}
				int homeAtStart = CountIn(home, name);
				int returnsAtStart = CaretakerJob.CargoReturns;
				int onTheWayAtStart = CaretakerJob.DroppedOnTheWay;
				CaretakerJob.MostStopsSeen = 0;
				Say($"Set up: {spawned} {ChestLabels.Pretty(name)} - {CountIn(strayA, name)} in strays A, {CountIn(strayB, name)} in strays B, {loose.Count} handful(s) on the floor; the settlement already had {fewest} of its own. " + $"Watching {caretakers.Count} caretaker(s) for up to {seconds:0} s. Stay near, but do not open the test chests.");
				float started = Time.time;
				float? strayAEmpty = null;
				float? strayBEmpty = null;
				float? floorClear = null;
				float? allHome = null;
				float nextNote = Time.time + 15f;
				while (Time.time - started < seconds)
				{
					yield return (object)new WaitForSeconds(1f);
					float num4 = Time.time - started;
					if ((Object)(object)((IEnumerable<GameObject>)made).FirstOrDefault((Func<GameObject, bool>)((GameObject chest) => (Object)(object)chest == (Object)null || (Object)(object)chest.GetComponent<ZNetView>() == (Object)null || !chest.GetComponent<ZNetView>().IsValid())) != (Object)null || made.Any((GameObject chest) => (Object)(object)chest == (Object)null))
					{
						Say("STOPPED  a test chest was removed, so the counts mean nothing from here. Cleaning up.");
						yield break;
					}
					if (_stop)
					{
						Say("STOPPED  by inn.test.caretaker stop. Cleaning up.");
						yield break;
					}
					int num5 = CountIn(strayA, name);
					int num6 = CountIn(strayB, name);
					int num7 = loose.Count((ItemDrop drop) => (Object)(object)drop != (Object)null && (Object)(object)drop.m_nview != (Object)null && drop.m_nview.IsValid());
					int num8 = CountIn(home, name) - homeAtStart;
					if (num5 == 0 && !strayAEmpty.HasValue)
					{
						strayAEmpty = num4;
					}
					if (num6 == 0 && !strayBEmpty.HasValue)
					{
						strayBEmpty = num4;
					}
					if (num7 == 0 && !floorClear.HasValue)
					{
						floorClear = num4;
					}
					if (num8 >= spawned && !allHome.HasValue)
					{
						allHome = num4;
					}
					if (Time.time >= nextNote)
					{
						nextNote = Time.time + 15f;
						string text2 = $"{num4:0} s: strays A {num5}, strays B {num6}, floor {num7}, home {Mathf.Max(0, num8)}/{spawned}";
						Say(text2);
						Player localPlayer = Player.m_localPlayer;
						if (localPlayer != null)
						{
							((Character)localPlayer).Message((MessageType)1, "Caretaker test - " + text2, 0, (Sprite)null, false);
						}
					}
					if (num5 == 0 && num6 == 0 && num7 == 0 && num8 >= spawned && caretakers.All((SettlerController c) => c.InventoryItemCount(name) == 0))
					{
						break;
					}
				}
				int num9 = CaretakerJob.CargoReturns - returnsAtStart;
				int num10 = CaretakerJob.DroppedOnTheWay - onTheWayAtStart;
				int num11 = Mathf.Max(0, CountIn(home, name) - homeAtStart);
				Say("---- caretaker test ----");
				Say(Line(strayAEmpty.HasValue && strayBEmpty.HasValue, "the chests told to give everything away were emptied", "A at " + At(strayAEmpty) + ", B at " + At(strayBEmpty)));
				Say(Line(floorClear.HasValue, "what was on the floor was picked up", "at " + At(floorClear)));
				int num12 = CountIn(strayA, name) + CountIn(strayB, name) + loose.Count((ItemDrop drop) => (Object)(object)drop != (Object)null && (Object)(object)drop.m_nview != (Object)null && drop.m_nview.IsValid()) + caretakers.Sum((SettlerController c) => c.InventoryItemCount(name));
				Say(Line(num11 >= spawned && num12 == 0, "all of it reached the chest it belongs in", $"{num11} arrived, {num12} still in the stray chests, on the floor or in a pack"));
				Say(Line(CaretakerJob.MostStopsSeen <= 6, "no collecting round stopped at more than six chests", $"the longest stopped at {CaretakerJob.MostStopsSeen}"));
				Say(Line(num9 == 0, "no sorting cargo had to be carried back", (num9 == 0) ? "none" : $"{num9} tick(s) carrying it back"));
				Say($"INFO  deliveries made on the way round: {num10}");
				if (num11 < spawned)
				{
					Where(caretakers, asking, name, home, strayA, strayB);
				}
				foreach (SettlerController item in caretakers)
				{
					Say("INFO  " + item.PanelName + ": \"" + Localization.instance.Localize(item.CurrentStatusText ?? string.Empty) + "\", " + $"carrying {item.InventoryItemCount(name)} {ChestLabels.Pretty(name)}");
				}
				void Fill(Container chest, int amount)
				{
					ItemData val6 = sample.Clone();
					val6.m_stack = amount;
					val6.m_dropPrefab = itemPrefab;
					chest.GetInventory().AddItem(val6);
				}
				Container Place(float angle, string label)
				{
					//IL_0001: 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_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)
					//IL_0026: Unknown result type (might be due to invalid IL or missing references)
					//IL_002b: Unknown result type (might be due to invalid IL or missing references)
					//IL_0030: 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_0151: Unknown result type (might be due to invalid IL or missing references)
					//IL_0153: Unknown result type (might be due to invalid IL or missing references)
					//IL_0158: Unknown result type (might be due to invalid IL or missing references)
					//IL_015d: 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_0097: 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_00b0: Unknown result type (might be due to invalid IL or missing references)
					//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
					//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
					//IL_00c1: 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_00d4: Unknown result type (might be due to invalid IL or missing references)
					//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
					//IL_011d: Unknown result type (might be due to invalid IL or missing references)
					//IL_0122: Unknown result type (might be due to invalid IL or missing references)
					Vector3 val6 = at + Quaternion.Euler(0f, angle, 0f) * ahead * 3f;
					val6.y = FloorAt(val6, at.y);
					bool flag = false;
					float[] array = new float[4] { 3f, 4f, 2.5f, 5f };
					foreach (float num13 in array)
					{
						float[] array2 = new float[5] { 0f, 20f, -20f, 40f, -40f };
						foreach (float num14 in array2)
						{
							Vector3 tried = at + Quaternion.Euler(0f, angle + num14, 0f) * ahead * num13;
							tried.y = FloorAt(tried, at.y);
							if (Free(tried) && made.All((GameObject other) => Vector3.Distance(other.transform.position, tried) > 1.5f))
							{
								val6 = tried;
								flag = true;
								break;
							}
						}
						if (flag)
						{
							break;
						}
					}
					GameObject val7 = Object.Instantiate<GameObject>(chestPrefab, val6, Quaternion.LookRotation(-ahead));
					Piece component = val7.GetComponent<Piece>();
					if (component != null)
					{
						component.SetCreator(player.GetPlayerID(), PlatformUserID.None);
					}
					made.Add(val7);
					return val7.GetComponent<Container>() ?? val7.GetComponentInChildren<Container>();
				}
			}
			finally
			{
				CleanUp(made, loose, name, spawned, home, strayA, strayB);
				_running = false;
			}
		}

		private static float FloorAt(Vector3 spot, float playerY)
		{
			//IL_0000: 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_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			RaycastHit val = default(RaycastHit);
			if (!Physics.Raycast(new Vector3(spot.x, playerY + 1.2f, spot.z), Vector3.down, ref val, 3.5f, Floors, (QueryTriggerInteraction)1))
			{
				return playerY;
			}
			return ((RaycastHit)(ref val)).point.y;
		}

		private static bool Free(Vector3 floor)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: 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_0010: 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)
			return !Physics.CheckBox(floor + Vector3.up * 0.55f, new Vector3(0.6f, 0.45f, 0.45f), Quaternion.identity, Solids, (QueryTriggerInteraction)1);
		}

		private static string Line(bool ok, string what, string detail)
		{
			return (ok ? "PASS  " : "FAIL  ") + what + " - " + detail;
		}

		private static string At(float? at)
		{
			if (at.HasValue)
			{
				return $"{at.Value:0} s";
			}
			return "never";
		}

		private static void Where(List<SettlerController> caretakers, SettlerController asking, string name, Container home, Container strayA, Container strayB)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			Say($"INFO  still in strays A: {CountIn(strayA, name)}, strays B: {CountIn(strayB, name)}");
			foreach (SettlerController caretaker in caretakers)
			{
				int num = caretaker.InventoryItemCount(name);
				if (num > 0)
				{
					Say($"INFO  {caretaker.PanelName} is carrying {num}");
				}
			}
			Vector3 position = ((Component)Player.m_localPlayer).transform.position;
			foreach (Container item in SettlementStorage.Managed(asking))
			{
				if (!((Object)(object)item == (Object)(object)home) && !((Object)(object)item == (Object)(object)strayA) && !((Object)(object)item == (Object)(object)strayB))
				{
					int num2 = CountIn(item, name);
					if (num2 > 0)
					{
						Say($"INFO  {num2} in {ChestLabels.AsDestination(item)} ({ChestLabels.Where(position, item)})");
					}
				}
			}
		}

		private static void CleanUp(List<GameObject> made, List<ItemDrop> loose, string name, int spawned, params Container[] chests)
		{
			if (made.Count == 0 && loose.Count == 0)
			{
				return;
			}
			int num = spawned;
			foreach (ItemDrop item in loose)
			{
				if ((Object)(object)item != (Object)null && (Object)(object)item.m_nview != (Object)null && item.m_nview.IsValid())
				{
					num -= item.m_itemData.m_stack;
					ZNetScene.instance.Destroy(((Component)item).gameObject);
				}
			}
			int num2 = 0;
			foreach (Container val in chests)
			{
				Inventory val2 = (((Object)(object)val != (Object)null) ? val.GetInventory() : null);
				if (val2 == null || name == null)
				{
					continue;
				}
				foreach (ItemData item2 in (from item in val2.GetAllItems()
					where item.m_shared.m_name == name
					select item).ToList())
				{
					if (num <= 0)
					{
						break;
					}
					int num3 = Mathf.Min(num, item2.m_stack);
					val2.RemoveItem(item2, num3);
					num -= num3;
				}
				num2 += val2.GetAllItems().Count;
				val.DropAllItems();
			}
			foreach (GameObject item3 in made)
			{
				if ((Object)(object)item3 != (Object)null)
				{
					ZNetScene.instance.Destroy(item3);
				}
			}
			Say($"Cleaned up: the test chests are gone. {Mathf.Max(0, num)} of the test's {ChestLabels.Pretty(name ?? string.Empty)} is still out in the settlement" + ((num2 > 0) ? $"; {num2} stack(s) of yours that had gone into the test chests are set down where they stood." : "."));
		}
	}
	internal sealed class ChestCensusCommand : ConsoleCommand
	{
		public override string Name => "inn.chests";

		public override string Help => "- every chest this settlement can use: what it holds, what it is for, how full";

		public override bool IsCheat => false;

		public override void Run(string[] args)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			Player player = Player.m_localPlayer;
			if ((Object)(object)player == (Object)null)
			{
				Console.instance.Print("Innangard: nobody is standing anywhere.");
				return;
			}
			SettlerController settlerController = Nearest(((Component)player).transform.position);
			if ((Object)(object)settlerController == (Object)null)
			{
				Console.instance.Print("Innangard: no resident nearby to ask. Stand in a settlement.");
				return;
			}
			List<Container> list = SettlementStorage.Managed(settlerController);
			if (list.Count == 0)
			{
				Console.instance.Print("Innangard: " + settlerController.PanelName + " can use no chest here at all. A chest must stand inside the settlement, be placed by a player, and be open to it.");
				return;
			}
			List<Container> intake = settlerController.ResolveIntakeStorages();
			list.Sort((Container left, Container right) => Vector3.Distance(((Component)player).transform.position, ((Component)left).transform.position).CompareTo(Vector3.Distance(((Component)player).transform.position, ((Component)right).transform.position)));
			int told = 0;
			int inferred = 0;
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			Console.instance.Print($"Innangard: {list.Count} chest(s) {settlerController.PanelName} can use.");
			foreach (Container item in list)
			{
				Inventory inventory = item.GetInventory();
				if (inventory != null)
				{
					int num4 = inventory.GetWidth() * inventory.GetHeight();
					int emptySlots = inventory.GetEmptySlots();
					num3 += num4 - emptySlots;
					num2 += emptySlots;
					if (emptySlots == 0)
					{
						num++;
					}
					string text = Rule(item, intake, ref told, ref inferred);
					Console.instance.Print($"  {Where(player, item)}  {num4 - emptySlots}/{num4} slots" + ((emptySlots == 0) ? " FULL" : string.Empty) + "  {" + text + "}");
					string text2 = Holding(inventory);
					if (text2.Length > 0)
					{
						Console.instance.Print("      " + text2);
					}
				}
			}
			Console.instance.Print($"  -- {told} told what to hold, {inferred} going by what is in them, " + $"{list.Count - told - inferred} saying nothing; {num} full; " + $"{num3} slot(s) used and {num2} free.");
			Console.instance.Print("  A chest that says nothing takes anything it already has more than one of, and an empty one takes anything at all. That is the rule a settlement runs on when nobody marks anything.");
			Console.instance.Print("  Set a chest to a kind and that kind is gathered into it from the chests that merely hold it. A chest set to take everything away is emptied.");
			Console.instance.Print("  A hand-picked list beats a kind: where two chests claim the same thing, the hand-picked one takes it all and the other stays empty. Clear the list to put them back on equal terms.");
			LeftAlone(player, settlerController);
		}

		private static void LeftAlone(Player player, SettlerController asking)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			List<Container> list = new List<Container>();
			Container[] array = SceneObjectCache.Get<Container>(1f);
			foreach (Container val in array)
			{
				if ((Object)(object)val != (Object)null && !(val is SettlerInventory) && ((Component)val).gameObject.activeInHierarchy && ChestSortRules.LeftAlone(val) && asking.InSettlementArea(((Component)val).transform.position))
				{
					list.Add(val);
				}
			}
			if (list.Count == 0)
			{
				return;
			}
			list.Sort((Container left, Container right) => Vector3.Distance(((Component)player).transform.position, ((Component)left).transform.position).CompareTo(Vector3.Distance(((Component)player).transform.position, ((Component)right).transform.position)));
			Console.instance.Print($"  {list.Count} chest(s) left alone - yours, inside the " + "settlement, and nobody here touches them:");
			foreach (Container item in list)
			{
				Inventory inventory = item.GetInventory();
				int num = ((inventory != null) ? (inventory.GetWidth() * inventory.GetHeight()) : 0);
				int num2 = ((inventory != null) ? inventory.GetEmptySlots() : 0);
				Console.instance.Print($"  {Where(player, item)}  {num - num2}/{num} slots" + "  {LEFT ALONE}");
			}
		}

		private static string Where(Player player, Container chest)
		{
			//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_0016: 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_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: 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_005f: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = ((Component)chest).transform.position - ((Component)player).transform.position;
			Vector2 val2 = new Vector2(val.x, val.z);
			float magnitude = ((Vector2)(ref val2)).magnitude;
			string arg = ((Mathf.Abs(val.y) < 1.5f) ? string.Empty : ((val.y > 0f) ? $" {val.y:0}m up" : $" {0f - val.y:0}m down"));
			return $"{magnitude,4:0}m {Compass(val)}{arg}".PadRight(14);
		}

		private static string Compass(Vector3 there)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Atan2(there.x, there.z) * 57.29578f;
			return (new string[8] { "N", "NE", "E", "SE", "S", "SW", "W", "NW" })[Mathf.RoundToInt(Mathf.Repeat(num, 360f) / 45f) % 8].PadRight(2);
		}

		private static string Rule(Container chest, List<Container> intake, ref int told, ref int inferred)
		{
			string text = (intake.Contains(chest) ? ", ticked for the settlement" : string.Empty);
			int num = ChestSortRules.KindsOf(chest);
			if (num != 0)
			{
				told++;
				return "for " + Kinds(num) + text;
			}
			if (ChestSortRules.IsConfigured(chest))
			{
				told++;
				HashSet<string> hashSet = ChestSortRules.ReadItems(chest);
				return ((hashSet.Count == 0) ? "OUTBOX - nothing belongs here, so everything in it is taken away" : ($"by hand: {hashSet.Count} item(s) - " + string.Join(", ", hashSet.Select(Pretty).Take(4)) + ((hashSet.Count > 4) ? ", ..." : string.Empty))) + text;
			}
			string text2 = Inferred(chest);
			if (text2.Length > 0)
			{
				inferred++;
				return "unmarked, reads as " + text2 + text;
			}
			return "unmarked and empty - a home for anything" + text;
		}

		private static string Kinds(int kinds)
		{
			List<string> list = new List<string>(4);
			StockCategories.Kind[] order = StockCategories.Order;
			for (int i = 0; i < order.Length; i++)
			{
				StockCategories.Kind kind = order[i];
				if ((kinds & (1 << (int)kind)) != 0)
				{
					list.Add(kind.ToString());
				}
			}
			if (list.Count <= 0)
			{
				return "nothing";
			}
			return string.Join("+", list);
		}

		private static string Inferred(Container chest)
		{
			Inventory inventory = chest.GetInventory();
			if (inventory == null)
			{
				return string.Empty;
			}
			List<string> list = new List<string>(4);
			HashSet<string> hashSet = new HashSet<string>();
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem?.m_shared != null && ChestSortRules.IsSortable(allItem) && hashSet.Add(allItem.m_shared.m_name) && ChestSortRules.Accepts(chest, allItem.m_shared.m_name))
				{
					list.Add(Pretty(allItem.m_shared.m_name));
				}
			}
			if (list.Count == 0)
			{
				return string.Empty;
			}
			return string.Join(", ", list.Take(4)) + ((list.Count > 4) ? ", ..." : string.Empty);
		}

		private static string Holding(Inventory inside)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (ItemData allItem in inside.GetAllItems())
			{
				if (allItem?.m_shared != null && allItem.m_stack > 0)
				{
					dictionary.TryGetValue(allItem.m_shared.m_name, out var value);
					dictionary[allItem.m_shared.m_name] = value + allItem.m_stack;
				}
			}
			if (dictionary.Count == 0)
			{
				return string.Empty;
			}
			return string.Join(", ", from entry in dictionary.OrderByDescending((KeyValuePair<string, int> entry) => entry.Value).Take(6)
				select $"{entry.Value} {Pretty(entry.Key)}") + ((dictionary.Count > 6) ? $", and {dictionary.Count - 6} more" : string.Empty);
		}

		private static string Pretty(string sharedName)
		{
			Localization instance = Localization.instance;
			string text = ((instance != null) ? instance.Localize(sharedName) : null);
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
			return sharedName;
		}

		private static SettlerController Nearest(Vector3 to)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			SettlerController result = null;
			float num = float.MaxValue;
			foreach (SettlerController instance in SettlerController.Instances)
			{
				if (!((Object)(object)instance == (Object)null) && instance.IsValidSettler)
				{
					float num2 = Vector3.Distance(to, instance.Position);
					if (num2 < num)
					{
						num = num2;
						result = instance;
					}
				}
			}
			return result;
		}
	}
	internal static class ChestContents
	{
		internal sealed class Summary
		{
			internal uint Revision;

			internal int Slots;

			internal readonly Dictionary<string, int> SortableSlots = new Dictionary<string, int>(StringComparer.Ordinal);

			internal readonly Dictionary<string, int> Counts = new Dictionary<string, int>(StringComparer.Ordinal);

			internal int UsedSortable;

			internal int MostSlotsOfOne;

			internal int Count(string sharedName)
			{
				if (!Counts.TryGetValue(sharedName, out var value))
				{
					return 0;
				}
				return value;
			}

			internal bool Dominates(string sharedName)
			{
				if (UsedSortable == 0)
				{
					return true;
				}
				SortableSlots.TryGetValue(sharedName, out var value);
				if (value == 0)
				{
					return false;
				}
				if (value <= 1)
				{
					return MostSlotsOfOne <= value;
				}
				return true;
			}
		}

		private static readonly Dictionary<Container, Summary> Known = new Dictionary<Container, Summary>();

		private static readonly List<Container> Gone = new List<Container>();

		private static float _nextPrune;

		internal static Summary Of(Container container)
		{
			Inventory val = ((container != null) ? container.GetInventory() : null);
			if (val == null)
			{
				return null;
			}
			PruneNow();
			uint lastRevision = container.m_lastRevision;
			int count = val.m_inventory.Count;
			if (Known.TryGetValue(container, out var value) && value.Revision == lastRevision && value.Slots == count)
			{
				return value;
			}
			if (value == null)
			{
				value = new Summary();
				Known[container] = value;
			}
			Count(val, value);
			value.Revision = lastRevision;
			value.Slots = count;
			return value;
		}

		private static void Count(Inventory inventory, Summary summary)
		{
			summary.SortableSlots.Clear();
			summary.Counts.Clear();
			summary.UsedSortable = 0;
			summary.MostSlotsOfOne = 0;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem?.m_shared == null)
				{
					continue;
				}
				string name = allItem.m_shared.m_name;
				if (allItem.m_stack > 0)
				{
					summary.Counts.TryGetValue(name, out var value);
					summary.Counts[name] = value + allItem.m_stack;
				}
				if (ChestSortRules.IsSortable(allItem))
				{
					summary.UsedSortable++;
					summary.SortableSlots.TryGetValue(name, out var value2);
					value2++;
					summary.SortableSlots[name] = value2;
					if (value2 > summary.MostSlotsOfOne)
					{
						summary.MostSlotsOfOne = value2;
					}
				}
			}
		}

		private static void PruneNow()
		{
			if (Time.unscaledTime < _nextPrune)
			{
				return;
			}
			_nextPrune = Time.unscaledTime + 30f;
			Gone.Clear();
			foreach (Container key in Known.Keys)
			{
				if ((Object)(object)key == (Object)null)
				{
					Gone.Add(key);
				}
			}
			foreach (Container item in Gone)
			{
				Known.Remove(item);
			}
			Gone.Clear();
		}
	}
	internal static class ChestLabels
	{
		internal enum Mode
		{
			Named,
			Mine,
			Kinds,
			List,
			Outbox,
			CatchAll,
			Guess,
			Empty
		}

		private struct Answer
		{
			internal float Until;

			internal string Line;
		}

		private const int Shown = 3;

		private static readonly Dictionary<string, Answer> Answers = new Dictionary<string, Answer>();

		private const float AnswerLasts = 1f;

		internal static string Core(Container chest, out Mode mode)
		{
			string text = ChestSortRules.LabelOf(chest);
			if (text.Length > 0)
			{
				mode = Mode.Named;
				return text;
			}
			if (ChestSortRules.LeftAlone(chest))
			{
				mode = Mode.Mine;
				return string.Empty;
			}
			if (ChestSortRules.IsCatchAll(chest))
			{
				mode = Mode.CatchAll;
				return string.Empty;
			}
			int kinds = ChestSortRules.KindsOf(chest);
			if (kinds != 0)
			{
				mode = Mode.Kinds;
				return Cut(from kind in StockCategories.Order
					where (kinds & (1 << (int)kind)) != 0
					select Localization.instance.Localize(StockCategories.TitleKey(kind)));
			}
			if (ChestSortRules.IsConfigured(chest))
			{
				HashSet<string> hashSet = ChestSortRules.ReadItems(chest);
				if (hashSet.Count == 0)
				{
					mode = Mode.Outbox;
					return string.Empty;
				}
				mode = Mode.List;
				return Cut(from name in hashSet.Select(Pretty)
					orderby name
					select name);
			}
			ChestContents.Summary summary = ChestContents.Of(chest);
			if (summary == null || summary.UsedSortable == 0)
			{
				mode = Mode.Empty;
				return string.Empty;
			}
			mode = Mode.Guess;
			return Cut(from entry in summary.SortableSlots
				where summary.Dominates(entry.Key)
				orderby entry.Value descending
				select Pretty(entry.Key));
		}

		internal static string HoverLine(Container chest)
		{
			Mode mode;
			string text = Core(chest, out mode);
			string text2;
			switch (mode)
			{
			case Mode.Named:
				text2 = "<color=#FFD27F>" + text + "</color>";
				break;
			case Mode.Mine:
				text2 = Localization.instance.Localize("$inn_chest_hover_mine");
				break;
			case Mode.Kinds:
			case Mode.List:
				text2 = string.Format(Localization.instance.Localize("$inn_chest_hover_for"), text);
				break;
			case Mode.Outbox:
				text2 = Localization.instance.Localize("$inn_chest_hover_outbox");
				break;
			case Mode.CatchAll:
				text2 = Localization.instance.Localize("$inn_chest_hover_catch_all");
				break;
			case Mode.Guess:
				text2 = string.Format(Localization.instance.Localize("$inn_chest_hover_guess"), text);
				break;
			default:
				text2 = Localization.instance.Localize("$inn_chest_hover_empty");
				break;
			}
			string text3 = text2;
			Inventory inventory = chest.GetInventory();
			if (inventory != null && inventory.GetEmptySlots() == 0)
			{
				text3 += Localization.instance.Localize("$inn_chest_hover_full");
			}
			return text3;
		}

		internal static string AsDestination(Container chest)
		{
			Mode mode;
			string text = Core(chest, out mode);
			switch (mode)
			{
			case Mode.Named:
				return text;
			case Mode.Kinds:
			case Mode.List:
				return string.Format(Localization.instance.Localize("$inn_chest_tip_chest_for"), text);
			case Mode.Guess:
				return string.Format(Localization.instance.Localize("$inn_chest_tip_chest_with"), text);
			case Mode.CatchAll:
				return Localization.instance.Localize("$inn_chest_tip_catch_all");
			default:
				return Localization.instance.Localize("$inn_chest_tip_an_empty_chest");
			}
		}

		internal static string Cut(IEnumerable<string> names)
		{
			List<string> list = names.Where((string name) => !string.IsNullOrEmpty(name)).ToList();
			if (list.Count <= 3)
			{
				return string.Join(", ", list);
			}
			return string.Join(", ", list.Take(3)) + $" … (+{list.Count - 3})";
		}

		internal static string Pretty(string sharedName)
		{
			Localization instance = Localization.instance;
			string text = ((instance != null) ? instance.Localize(sharedName) : null);
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
			return sharedName;
		}

		internal static string Where(Vector3 from, Container chest)
		{
			//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_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)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = ((Component)chest).transform.position - from;
			Vector2 val2 = new Vector2(val.x, val.z);
			float magnitude = ((Vector2)(ref val2)).magnitude;
			if (magnitude < 2f)
			{
				return Localization.instance.Localize("$inn_chest_tip_here");
			}
			float num = Mathf.Atan2(val.x, val.z) * 57.29578f;
			string[] array = new string[8] { "N", "NE", "E", "SE", "S", "SW", "W", "NW" };
			return $"{magnitude:0} m {array[Mathf.RoundToInt(Mathf.Repeat(num, 360f) / 45f) % 8]}";
		}

		internal static string BelongsLine(ItemData item)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || item?.m_shared == null || !ChestSortRules.IsSortable(item) || (Object)(object)InventoryGui.instance == (Object)null || !InventoryGui.IsVisible())
			{
				return string.Empty;
			}
			Container currentContainer = InventoryGui.instance.m_currentContainer;
			bool flag = (Object)(object)currentContainer != (Object)null && !(currentContainer is SettlerInventory) && currentContainer.GetInventory() != null && currentContainer.GetInventory().ContainsItem(item);
			string key = item.m_shared.m_name + "|" + (flag ? ((Object)currentContainer).GetInstanceID().ToString() : "-");
			if (Answers.TryGetValue(key, out var value) && Time.unscaledTime < value.Until)
			{
				return value.Line;
			}
			if (Answers.Count > 256)
			{
				Answers.Clear();
			}
			string text = WorkOut(localPlayer, item, flag ? currentContainer : null);
			Answers[key] = new Answer
			{
				Until = Time.unscaledTime + 1f,
				Line = text
			};
			return text;
		}

		private static string WorkOut(Player player, ItemData item, Container heldIn)
		{
			//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_000c: 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_0064: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)player).transform.position;
			SettlerController settlerController = ResidentHere(position);
			if ((Object)(object)settlerController == (Object)null)
			{
				return string.Empty;
			}
			bool stays;
			bool fullAtRule;
			Container val = Destination(settlerController, item, heldIn, position, out stays, out fullAtRule);
			string text = (stays ? Localization.instance.Localize("$inn_chest_tip_stays") : ((!((Object)(object)val != (Object)null)) ? Localization.instance.Localize(fullAtRule ? "$inn_chest_tip_full" : "$inn_chest_tip_nowhere") : string.Format(Localization.instance.Localize("$inn_chest_tip_belongs"), AsDestination(val), Where(position, val))));
			return "\n<color=#C8B896>" + text + "</color>";
		}

		internal static Container Destination(SettlerController asking, ItemData item, Container heldIn, Vector3 here, out bool stays, out bool fullAtRule)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			string name = item.m_shared.m_name;
			List<Container> list = SettlementStorage.Managed(asking);
			Container val = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_currentContainer : null);
			bool flag = (Object)(object)val != (Object)null && !(val is SettlerInventory) && !list.Contains(val) && ChestSortRules.IsStationaryPlayerChest(val) && !ChestSortRules.LeftAlone(val) && !TradeChest.Is(val) && asking.InSettlementArea(((Component)val).transform.position) && val.CheckAccess(asking.SettlementOwnerId);
			if (flag)
			{
				list.Add(val);
			}
			int num = (((Object)(object)heldIn != (Object)null && (flag || list.Contains(heldIn))) ? ChestSortRules.Precision(heldIn, name) : int.MaxValue);
			Container val2 = null;
			int num2 = int.MaxValue;
			float num3 = float.MaxValue;
			fullAtRule = false;
			foreach (Container item2 in list)
			{
				if ((Object)(object)item2 == (Object)(object)heldIn)
				{
					continue;
				}
				int num4 = ChestSortRules.Precision(item2, name);
				if (num4 == int.MaxValue || num4 > num2)
				{
					continue;
				}
				Inventory inventory = item2.GetInventory();
				if (inventory == null)
				{
					continue;
				}
				int num5 = inventory.FindFreeStackSpace(name, (float)item.m_worldLevel);
				if (num5 + inventory.GetEmptySlots() * item.m_shared.m_maxStackSize <= 0)
				{
					fullAtRule |= num4 < 6;
					continue;
				}
				float num6 = ((num5 > 0) ? 0f : 1f) + Vector3.Distance(here, ((Component)item2).transform.position) * 0.001f;
				if (num4 < num2 || num6 < num3)
				{
					num2 = num4;
					num3 = num6;
					val2 = item2;
				}
			}
			stays = num != int.MaxValue && ((Object)(object)val2 == (Object)null || num2 >= num);
			if (!stays)
			{
				return val2;
			}
			return null;
		}

		internal static SettlerController ResidentHere(Vector3 here)
		{
			//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_0039: Unknown result type (might be due to invalid IL or missing references)
			SettlerController result = null;
			float num = float.MaxValue;
			foreach (SettlerController instance in SettlerController.Instances)
			{
				if (!((Object)(object)instance == (Object)null) && instance.IsValidSettler && instance.InSettlementArea(here))
				{
					float num2 = Vector3.Distance(here, instance.Position);
					if (num2 < num)
					{
						num = num2;
						result = instance;
					}
				}
			}
			return result;
		}
	}
	internal sealed class ChestSortPanel : MonoBehaviour
	{
		private sealed class ItemChoice
		{
			internal string SharedName;

			internal string DisplayName;

			internal Sprite Icon;

			internal Button Button;
		}

		private sealed class Copied
		{
			internal int Kinds;

			internal bool Configured;

			internal bool CatchAll;

			internal HashSet<string> Items;

			internal string Said;
		}

		[HarmonyPatch(typeof(Container), "Interact")]
		private static class OpenPatch
		{
			[HarmonyPrefix]
			private static bool Prefix(Container __instance, Humanoid character, bool hold, ref bool __result)
			{
				if (!hold)
				{
					Player val = (Player)(object)((character is Player) ? character : null);
					if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer))
					{
						if (!ModifierHeld() || !ChestSortRules.CanConfigure(__instance, val.GetPlayerID()))
						{
							return true;
						}
						Toggle(__instance);
						__result = true;
						return false;
					}
				}
				return true;
			}

			private static bool ModifierHeld()
			{
				return InnInput.ModifierHeld();
			}
		}

		private const float PanelWidth = 780f;

		private const float PanelHeight = 880f;

		private const float LeastHeight = 260f;

		private const float TitleHeight = 40f;

		private const float Edge = 14f;

		private const float Gap = 8f;

		private const float MaxUseDistance = 10f;

		private const float BetweenRefreshes = 0.25f;

		private const float ItemButtonHeight = 34f;

		private const float ListLeastHeight = 160f;

		private const float IconSize = 28f;

		private static readonly (IntakeRoles Role, string LabelKey)[] IntakeChoices = new(IntakeRoles, string)[11]
		{
			(IntakeRoles.Lumberjack, "$inn_trade_lumberjack"),
			(IntakeRoles.Miner, "$inn_trade_miner"),
			(IntakeRoles.Farmer, "$inn_trade_farmer"),
			(IntakeRoles.Forager, "$inn_trade_forager"),
			(IntakeRoles.Hunter, "$inn_trade_hunter"),
			(IntakeRoles.Fisherman, "$inn_role_fisherman"),
			(IntakeRoles.Cook, "$inn_trade_cook"),
			(IntakeRoles.Builder, "$inn_role_builder"),
			(IntakeRoles.Guard, "$inn_trade_guard"),
			(IntakeRoles.Follower, "$inn_trade_follower"),
			(IntakeRoles.Smith, "$inn_trade_smith")
		};

		private static ChestSortPanel _instance;

		private Container _container;

		private GameObject _panel;

		private bool _blocking;

		private float _heightNow = -1f;

		private float _scaledAt = -1f;

		private float _nextRefresh;

		private bool _wasConfigured;

		private HashSet<string> _selected = new HashSet<string>(StringComparer.Ordinal);

		private readonly List<ItemChoice> _choices = new List<ItemChoice>();

		private Text _intakeState;

		private Text _intakeNoFrithstone;

		private RectTransform _intakeControls;

		private Button _intakeRemove;

		private Button _intakeAll;

		private readonly List<Button> _roleButtons = new List<Button>();

		private Text _origin;

		private InputField _search;

		private RectTransform _grid;

		private Text _noMatch;

		private Text _chosenCount;

		private readonly List<Button> _kindButtons = new List<Button>();

		private Button _anything;

		private Button _byHand;

		private Button _outbox;

		private Button _catchAll;

		private Button _mine;

		private Text _mineHelp;

		private RectTransform _picking;

		private RectTransform _intakeGrid;

		private Button _intakeChoose;

		private bool _showingCrafts;

		private int _kinds;

		private InputField _label;

		private static Copied _copied;

		private Button _paste;

		private GameObject _footer;

		private RectTransform _list;

		private ScrollRect _scroll;

		private RectTransform _column;

		private bool _remeasure;

		private LayoutElement _slack;

		private static SettlerRole TradeOf(IntakeRoles role)
		{
			return role switch
			{
				IntakeRoles.Lumberjack => SettlerRole.Lumberjack, 
				IntakeRoles.Miner => SettlerRole.Miner, 
				IntakeRoles.Farmer => SettlerRole.Farmer, 
				IntakeRoles.Forager => SettlerRole.Forager, 
				IntakeRoles.Hunter => SettlerRole.Hunter, 
				IntakeRoles.Fisherman => SettlerRole.Fisherman, 
				IntakeRoles.Cook => SettlerRole.Cook, 
				IntakeRoles.Builder => SettlerRole.Builder, 
				IntakeRoles.Guard => SettlerRole.Guard, 
				IntakeRoles.Follower => SettlerRole.Follower, 
				IntakeRoles.Smith => SettlerRole.Smith, 
				_ => SettlerRole.Idle, 
			};
		}

		internal static void Toggle(Container container)
		{
			if ((Object)(object)_instance == (Object)null)
			{
				GameObject val = (((Object)(object)InnangardPlugin.Instance != (Object)null) ? ((Component)InnangardPlugin.Instance).gameObject : null);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				_instance = val.AddComponent<ChestSortPanel>();
			}
			if ((Object)(object)_instance._container == (Object)(object)container)
			{
				_instance.Close();
			}
			else
			{
				_instance.Open(container);
			}
		}

		private void Awake()
		{
			GUIManager.OnCustomGUIAvailable += Forget;
		}

		private void OnDestroy()
		{
			GUIManager.OnCustomGUIAvailable -= Forget;
			Release();
			if ((Object)(object)_instance == (Object)(object)this)
			{
				_instance = null;
			}
		}

		private void Forget()
		{
			_panel = null;
			_heightNow = -1f;
			_container = null;
			_choices.Clear();
			_roleButtons.Clear();
			_kindButtons.Clear();
			_scaledAt = -1f;
			Release();
		}

		private void Open(Container container)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !ChestSortRules.CanConfigure(container, localPlayer.GetPlayerID()))
			{
				return;
			}
			try
			{
				if (Build())
				{
					_container = container;
					FrithLore.Tell("inn_lore_chest");
					_wasConfigured = ChestSortRules.IsConfigured(container);
					_kinds = ChestSortRules.KindsOf(container);
					_selected = ChestSortRules.ReadItems(container);
					Populate(BuildChoices(container, _selected));
					_search.text = string.Empty;
					_label.text = ChestSortRules.LabelOf(container);
					((Component)_picking).gameObject.SetActive(_wasConfigured);
					_showingCrafts = false;
					_remeasure = true;
					_panel.SetActive(true);
					Block();
					Refresh();
				}
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("Innangard[stores]: the sorting window failed to open: " + ex));
				Close();
			}
		}

		private void Close()
		{
			SaveLabel();
			_container = null;
			if ((Object)(object)_panel != (Object)null)
			{
				_panel.SetActive(false);
			}
			Release();
		}

		private void Block()
		{
			if (!_blocking)
			{
				_blocking = true;
				GUIManager.BlockInput(true);
			}
		}

		private void Release()
		{
			if (_blocking)
			{
				_blocking = false;
				GUIManager.BlockInput(false);
			}
		}

		private void Update()
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_container == (Object)null)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !((Component)_container).gameObject.activeInHierarchy || Vector3.Distance(((Component)localPlayer).transform.position, ((Component)_container).transform.position) > 10f || Input.GetKeyDown((KeyCode)27))
			{
				Close();
				return;
			}
			Cursor.lockState = (CursorLockMode)0;
			Cursor.visible = true;
			if (Time.unscaledTime >= _nextRefresh)
			{
				_nextRefresh = Time.unscaledTime + 0.25f;
				Refresh();
			}
		}

		private bool Build()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Expected O, but got Unknown
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_panel != (Object)null)
			{
				return true;
			}
			Transform val = (((Object)(object)GUIManager.CustomGUIFront != (Object)null) ? GUIManager.CustomGUIFront.transform : null);
			if ((Object)(object)val == (Object)null || GUIManager.Instance == null)
			{
				return false;
			}
			_panel = GUIManager.Instance.CreateWoodpanel(val, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 780f, 880f, true);
			if ((Object)(object)_panel == (Object)null)
			{
				return false;
			}
			((Object)_panel).name = "inn_chest_panel";
			RectTransform obj = Child("inn_chest_title", _panel.transform);
			obj.anchorMin = new Vector2(0f, 1f);
			obj.anchorMax = new Vector2(1f, 1f);
			obj.pivot = new Vector2(0.5f, 1f);
			obj.anchoredPosition = new Vector2(0f, -14f);
			obj.sizeDelta = new Vector2(-28f, 40f);
			InnangardGui.Fill(((Graphic)InnangardGui.Write((Transform)(object)obj, Localization.instance.Localize("$inn_stores_filter_title"), 22, GUIManager.Instance.ValheimOrange, (TextAnchor)4)).rectTransform);
			((UnityEvent)GUIManager.Instance.CreateButton("x", _panel.transform, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-30f, -30f), 32f, 32f).GetComponent<Button>().onClick).AddListener(new UnityAction(Close));
			RectTransform obj2 = Child("inn_chest_body", _panel.transform);
			obj2.offsetMin = new Vector2(14f, 14f);
			obj2.offsetMax = new Vector2(-14f, -62f);
			RectTransform column = (_column = InnangardGui.Stack(obj2, 8f, fits: false, (TextAnchor)0));
			BuildNaming(column);
			BuildIntake(column);
			BuildFilter(column);
			Flexible(column, ((Component)_picking).gameObject);
			Flexible(_picking, ((Component)_list).gameObject);
			((Component)_list).GetComponent<LayoutElement>().minHeight = 160f;
			return true;
		}

		private void Remeasure()
		{
			if (_remeasure && !((Object)(object)_column == (Object)null))
			{
				_remeasure = false;
				Canvas.ForceUpdateCanvases();
				LayoutRebuilder.ForceRebuildLayoutImmediate(_column);
				Canvas.ForceUpdateCanvases();
				LayoutRebuilder.ForceRebuildLayoutImmediate(_column);
			}
		}

		private static void Flexible(RectTransform column, GameObject grows)
		{
			for (int i = 0; i < ((Transform)column).childCount; i++)
			{
				GameObject gameObject = ((Component)((Transform)column).GetChild(i)).gameObject;
				LayoutElement val = gameObject.GetComponent<LayoutElement>() ?? gameObject.AddComponent<LayoutElement>();
				val.flexibleHeight = (((Object)(object)gameObject == (Object)(object)grows) ? 1f : 0f);
				val.minHeight = (((Object)(object)gameObject == (Object)(object)grows) ? 0f : val.minHeight);
			}
		}

		private void BuildIntake(RectTransform column)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = InnangardGui.Card(InnangardGui.Section(column, "$inn_stores_intake_section"), 4f);
			InnangardGui.Note(val, Localization.instance.Localize("$inn_stores_intake_help"));
			_intakeNoFrithstone = InnangardGui.Note(val, Localization.instance.Localize("$inn_stores_intake_no_frithstone"));
			((Graphic)_intakeNoFrithstone).color = InnangardGui.Warning;
			_intakeControls = InnangardGui.Stack(val, 4f, fits: false, (TextAnchor)0);
			RowWidget rowWidget = InnangardGui.Row(_intakeControls, string.Empty);
			_intakeState = rowWidget.Label;
			_intakeAll = rowWidget.Button("$inn_stores_intake_all", 130f, delegate
			{
				_showingCrafts = false;
				RequestIntake(IntakeRoles.All, said: true);
			});
			_intakeRemove = rowWidget.Button("$inn_stores_intake_remove", 190f, delegate
			{
				_showingCrafts = false;
				RequestIntake(IntakeRoles.None, said: false);
			});
			_intakeChoose = rowWidget.Button("$inn_stores_intake_choose", 170f, delegate
			{
				_showingCrafts = !_showingCrafts;
				_remeasure = true;
				Refresh();
			});
			_intakeGrid = InnangardGui.Grid(_intakeControls, 3, 190f, 30f);
			RectTransform intakeGrid = _intakeGrid;
			(IntakeRoles, string)[] intakeChoices = IntakeChoices;
			for (int num = 0; num < intakeChoices.Length; num++)
			{
				(IntakeRoles, string) tuple = intakeChoices[num];
				IntakeRoles item = tuple.Item1;
				string item2 = tuple.Item2;
				IntakeRoles which = item;
				Button item3 = InnangardGui.Press(intakeGrid, Localization.instance.Localize(item2), 190f, 30f, delegate
				{
					SettlementIntake settlementIntake = CurrentIntake();
					if (settlementIntake != null && (Object)(object)_container != (Object)null)
					{
						IntakeRoles intakeRoles = settlementIntake.RolesOf(_container);
						settlementIntake.Request(_container, ((intakeRoles & which) != IntakeRoles.None) ? (intakeRoles & ~which) : (intakeRoles | which));
					}
				});
				_roleButtons.Add(item3);
			}
		}

		private void BuildNaming(RectTransform column)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			RowWidget rowWidget = InnangardGui.Row(column, "$inn_stores_label", 34f);
			GameObject val = GUIManager.Instance.CreateInputField((Transform)(object)rowWidget.Controls, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), Vector2.zero, (ContentType)0, Localization.instance.Localize("$inn_stores_label_placeholder"), 16, 320f, 28f);
			LayoutElement obj = val.AddComponent<LayoutElement>();
			obj.preferredWidth = 320f;
			obj.minWidth = 320f;
			obj.flexibleWidth = 0f;
			obj.preferredHeight = 28f;
			_label = val.GetComponent<InputField>();
			_label.characterLimit = 32;
			((UnityEvent<string>)(object)_label.onEndEdit).AddListener((UnityAction<string>)delegate
			{
				SaveLabel();
			});
			rowWidget.Button("×", 32f, delegate
			{
				_label.text = string.Empty;
				_label.DeactivateInputField();
				SaveLabel();
			});
		}

		private void BuildFilter(RectTransform column)
		{
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			RowWidget rowWidget = InnangardGui.Row(column, string.Empty);
			rowWidget.Button("$inn_stores_copy", 200f, CopyRule);
			_paste = rowWidget.Button("$inn_stores_paste_nothing", 360f, PasteRule);
			_origin = InnangardGui.Title(column, string.Empty);
			InnangardGui.Note(column, Localization.instance.Localize("$inn_stores_filter_help"));
			RectTransform parent = InnangardGui.Grid(column, 3, 190f, 30f);
			StockCategories.Kind[] order = StockCategories.Order;
			foreach (StockCategories.Kind kind in order)
			{
				StockCategories.Kind mine = kind;
				_kindButtons.Add(InnangardGui.Press(parent, Localization.instance.Localize(StockCategories.TitleKey(mine)), 190f, 30f, delegate
				{
					Flip(mine);
				}));
			}
			InnangardGui.Note(column, Localization.instance.Localize("$inn_stores_filter_several_kinds"));
			RowWidget rowWidget2 = InnangardGui.Row(column, string.Empty);
			_anything = rowWidget2.Button("$inn_stores_filter_use_contents", 240f, delegate
			{
				if ((Object)(object)_container != (Object)null)
				{
					ChestSortRules.RequestSet(_container, Array.Empty<string>(), configured: false);
					Player localPlayer = Player.m_localPlayer;
					if (localPlayer != null)
					{
						((Character)localPlayer).Message((MessageType)2, "$inn_stores_filter_back_to_auto", 0, (Sprite)null, false);
					}
				}
				Close();
			});
			_byHand = rowWidget2.Button("$inn_stores_filter_by_name", 240f, delegate
			{
				bool flag = !((Component)_picking).gameObject.activeSelf;
				if (flag && (Object)(object)_container != (Object)null && _selected.Count == 0 && !ChestSortRules.IsConfigured(_container) && !ChestSortRules.IsForKinds(_container))
				{
					SelectPresentMaterials();
					Paint();
				}
				((Component)_picking).gameObject.SetActive(flag);
				_remeasure = true;
				Refresh();
			});
			RowWidget rowWidget3 = InnangardGui.Row(column, string.Empty);
			_outbox = rowWidget3.Button("$inn_stores_filter_outbox", 300f, delegate
			{
				if ((Object)(object)_container != (Object)null)
				{
					ChestSortRules.RequestSet(_container, Array.Empty<string>(), configured: true);
					Player localPlayer = Player.m_localPlayer;
					if (localPlayer != null)
					{
						((Character)localPlayer).Message((MessageType)2, "$inn_stores_filter_outbox_set", 0, (Sprite)null, false);
					}
				}
				Close();
			});
			InnangardGui.Note(column, Localization.instance.Localize("$inn_stores_filter_outbox_help"));
			RowWidget rowWidget4 = InnangardGui.Row(column, string.Empty);
			_catchAll = rowWidget4.Button("$inn_stores_filter_catch_all", 300f, delegate
			{
				if ((Object)(object)_container != (Object)null)
				{
					ChestSortRules.RequestCatchAll(_container);
					Player localPlayer = Player.m_localPlayer;
					if (localPlayer != null)
					{
						((Character)localPlayer).Message((MessageType)2, "$inn_stores_filter_catch_all_set", 0, (Sprite)null, false);
					}
				}
				Close();
			});
			InnangardGui.Note(column, Localization.instance.Localize("$inn_stores_filter_catch_all_help"));
			RectTransform val = InnangardGui.Section(column, "$inn_stores_private_section");
			RowWidget rowWidget5 = InnangardGui.Row(val, string.Empty);
			_mine = rowWidget5.Button("$inn_stores_filter_leave_alone", 300f, delegate
			{
				if ((Object)(object)_container != (Object)null)
				{
					bool flag = !ChestSortRules.LeftAlone(_container);
					ChestSortRules.RequestLeaveAlone(_container, flag);
					Player localPlayer = Player.m_localPlayer;
					if (localPlayer != null)
					{
						((Character)localPlayer).Message((MessageType)2, flag ? "$inn_stores_filter_left_alone" : "$inn_stores_filter_shared_again", 0, (Sprite)null, false);
					}
				}
				Close();
			});
			_mineHelp = InnangardGui.Note(val, Localization.instance.Localize("$inn_stores_filter_leave_alone_help"));
			_picking = InnangardGui.Stack(column, 8f, fits: false, (TextAnchor)0);
			RowWidget rowWidget6 = InnangardGui.Row(_picking, "$inn_stores_sort_search", 34f);
			GameObject val2 = GUIManager.Instance.CreateInputField((Transform)(object)rowWidget6.Controls, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), Vector2.zero, (ContentType)0, string.Empty, 16, 320f, 28f);
			LayoutElement obj = val2.AddComponent<LayoutElement>();
			obj.preferredWidth = 320f;
			obj.minWidth = 320f;
			obj.flexibleWidth = 0f;
			obj.preferredHeight = 28f;
			_search = val2.GetComponent<InputField>();
			_search.characterLimit = 24;
			((UnityEvent<string>)(object)_search.onValueChanged).AddListener((UnityAction<string>)delegate
			{
				Sort();
			});
			rowWidget6.Button("×", 32f, delegate
			{
				_search.text = string.Empty;
				_search.DeactivateInputField();
				Sort();
			});
			RowWidget rowWidget7 = InnangardGui.Row(_picking, string.Empty, 34f);
			rowWidget7.Button("$inn_stores_filter_from_inside", 280f, delegate
			{
				int count = _selected.Count;
				SelectPresentMaterials();
				Paint();
				Diagnostics.Log($"Innangard[stores]: took the list from the chest - {_selected.Count - count} " + $"new name(s), {_selected.Count} chosen, {_choices.Count} row(s) on offer");
			});
			rowWidget7.Button("$inn_stores_filter_start_over", 150f, delegate
			{
				_selected.Clear();
				Paint();
			});
			RectTransform val3 = InnangardGui.Scroller(_picking, 4f);
			_scroll = ((Component)val3).GetComponentInParent<ScrollRect>();
			_list = (((Object)(object)_scroll != (Object)null) ? ((Component)_scroll).GetComponent<RectTransform>() : val3);
			Transform val4 = (Transform)(object)val3;
			while ((Object)(object)val4.parent != (Object)null && (Object)(object)val4.parent != (Object)(object)_picking)
			{
				val4 = val4.parent;
			}
			_list = (RectTransform)(object)((val4 is RectTransform) ? val4 : null);
			_grid = InnangardGui.Grid(val3, 3, 240f, 34f);
			_noMatch = InnangardGui.Note(val3, Localization.instance.Localize("$inn_stores_filter_no_match"));
			RowWidget rowWidget8 = InnangardGui.Row(_picking, string.Empty, 36f);
			_chosenCount = rowWidget8.Label;
			rowWidget8.Button("$inn_stores_filter_keep", 160f, delegate
			{
				if ((Object)(object)_container != (Object)null)
				{
					ChestSortRules.RequestSet(_container, _selected, configured: true);
					Player localPlayer = Player.m_localPlayer;
					if (localPlayer != null)
					{
						((Character)localPlayer).Message((MessageType)2, "$inn_stores_filter_kept", 0, (Sprite)null, false);
					}
				}
				Close();
			});
			rowWidget8.Button("$inn_tend_close", 90f, Close);
			RectTransform val5 = Child("inn_chest_slack", (Transform)(object)column);
			_slack = ((Component)val5).gameObject.AddComponent<LayoutElement>();
			_slack.minHeight = 0f;
			_slack.preferredHeight = 0f;
			RowWidget rowWidget9 = InnangardGui.Row(column, string.Empty, 36f);
			rowWidget9.Button("$inn_tend_close", 90f, Close);
			_footer = ((Component)rowWidget9.Root).gameObject;
		}

		private void Populate(List<ItemChoice> choices)
		{
			for (int num = ((Transform)_grid).childCount - 1; num >= 0; num--)
			{
				Object.Destroy((Object)(object)((Component)((Transform)_grid).GetChild(num)).gameObject);
			}
			_choices.Clear();
			foreach (ItemChoice choice in choices)
			{
				ItemChoice mine = choice;
				mine.Button = InnangardGui.Press(_grid, choice.DisplayName, 240f, 34f, delegate
				{
					if (!_selected.Remove(mine.SharedName))
					{
						_selected.Add(mine.SharedName);
					}
					Paint();
				});
				IconOn(mine.Button, choice.Icon);
				_choices.Add(mine);
			}
			Sort();
			Paint();
			((MonoBehaviour)this).StartCoroutine(ScrollToTop());
		}

		private IEnumerator ScrollToTop()
		{
			yield return null;
			if ((Object)(object)_scroll != (Object)null)
			{
				Canvas.ForceUpdateCanvases();
				_scroll.verticalNormalizedPosition = 1f;
			}
		}

		private static void IconOn(Button button, Sprite icon)
		{
			//IL_0028: 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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			Text componentInChildren = ((Component)button).GetComponentInChildren<Text>();
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.alignment = (TextAnchor)3;
				((Graphic)componentInChildren).rectTransform.offsetMin = new Vector2(44f, ((Graphic)componentInChildren).rectTransform.offsetMin.y);
			}
			if (!((Object)(object)icon == (Object)null))
			{
				GameObject val = new GameObject("inn_icon", new Type[2]
				{
					typeof(RectTransform),
					typeof(Image)
				})
				{
					layer = 5
				};
				RectTransform component = val.GetComponent<RectTransform>();
				((Transform)component).SetParent(((Component)button).transform, false);
				((Transform)component).localScale = Vector3.one;
				component.anchorMin = new Vector2(0f, 0.5f);
				component.anchorMax = new Vector2(0f, 0.5f);
				component.pivot = new Vector2(0f, 0.5f);
				component.anchoredPosition = new Vector2(8f, 0f);
				component.sizeDelta = new Vector2(28f, 28f);
				Image component2 = val.GetComponent<Image>();
				component2.sprite = icon;
				component2.preserveAspect = true;
				((Graphic)component2).raycastTarget = false;
			}
		}

		private void Refresh()
		{
			//IL_0043: 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)
			if ((Object)(object)_container == (Object)null || (Object)(object)_panel == (Object)null)
			{
				return;
			}
			float scale = InnGuiSkin.Scale;
			if (!Mathf.Approximately(scale, _scaledAt))
			{
				_scaledAt = scale;
				_panel.transform.localScale = Vector3.one * scale;
			}
			SettlementIntake settlementIntake = CurrentIntake();
			bool flag = settlementIntake != null;
			((Component)_intakeNoFrithstone).gameObject.SetActive(!flag);
			((Component)_intakeControls).gameObject.SetActive(flag);
			if (flag)
			{
				IntakeRoles intakeRoles = settlementIntake.RolesOf(_container);
				bool flag2 = intakeRoles != IntakeRoles.None;
				_intakeState.text = Localization.instance.Localize(flag2 ? "$inn_stores_intake_current" : "$inn_stores_intake_available");
				((Component)_intakeRemove).gameObject.SetActive(flag2);
				InnangardGui.Chosen(_intakeAll, intakeRoles == IntakeRoles.All);
				bool flag3 = flag2 && intakeRoles != IntakeRoles.All;
				bool flag4 = _showingCrafts || flag3;
				if (((Component)_intakeGrid).gameObject.activeSelf != flag4)
				{
					((Component)_intakeGrid).gameObject.SetActive(flag4);
					_remeasure = true;
				}
				((Component)_intakeChoose).gameObject.SetActive(!flag3);
				for (int i = 0; i < _roleButtons.Count && i < IntakeChoices.Length; i++)
				{
					InnangardGui.Chosen(_roleButtons[i], (intakeRoles & IntakeChoices[i].Role) != 0);
					InnangardGui.Usable(_roleButtons[i], SettlerRoles.Offered(TradeOf(IntakeChoices[i].Role)));
				}
			}
			_kinds = ChestSortRules.KindsOf(_container);
			_wasConfigured = ChestSortRules.IsConfigured(_container);
			bool flag5 = ChestSortRules.IsCatchAll(_container);
			_origin.text = (flag5 ? Localization.instance.Localize("$inn_stores_filter_is_catch_all") : ((_kinds != 0) ? string.Format(Localization.instance.Localize("$inn_stores_filter_of_a_kind"), KindsNamed()) : Localization.instance.Localize((!_wasConfigured) ? "$inn_stores_filter_from_contents" : ((ChestSortRules.ReadItems(_container).Count == 0) ? "$inn_stores_filter_is_outbox" : "$inn_stores_filter_by_hand"))));
			for (int j = 0; j < _kindButtons.Count && j < StockCategories.Order.Length; j++)
			{
				InnangardGui.Chosen(_kindButtons[j], (_kinds & (1 << (int)StockCategories.Order[j])) != 0);
			}
			InnangardGui.Say(_paste, (_copied == null) ? "$inn_stores_paste_nothing" : string.Format(Localization.instance.Localize("$inn_stores_paste"), _copied.Said));
			InnangardGui.Usable(_paste, _copied != null);
			bool flag6 = ChestSortRules.LeftAlone(_container);
			InnangardGui.Chosen(_mine, flag6);
			InnangardGui.Say(_mine, flag6 ? "$inn_stores_filter_share_again" : "$inn_stores_filter_leave_alone");
			if ((Object)(object)_mineHelp != (Object)null)
			{
				_mineHelp.text = Localization.instance.Localize(flag6 ? "$inn_stores_filter_leave_alone_on" : "$inn_stores_filter_leave_alone_help");
			}
			if (flag6)
			{
				_origin.text = Localization.instance.Localize("$inn_stores_filter_is_left_alone");
			}
			bool flag7 = _wasConfigured && ChestSortRules.ReadItems(_container).Count == 0;
			InnangardGui.Chosen(_outbox, flag7);
			InnangardGui.Chosen(_catchAll, flag5);
			InnangardGui.Chosen(_anything, _kinds == 0 && !_wasConfigured && !flag5);
			InnangardGui.Chosen(_byHand, (_wasConfigured && !flag7) || ((Component)_picking).gameObject.activeSelf);
			_slack.flexibleHeight = (((Component)_picking).gameObject.activeSelf ? 0f : 1f);
			if ((Object)(object)_footer != (Object)null && _footer.activeSelf == ((Component)_picking).gameObject.activeSelf)
			{
				_footer.SetActive(!((Component)_picking).gameObject.activeSelf);
				_remeasure = true;
			}
			Remeasure();
			Resize();
		}

		private void Resize()
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = (((Object)(object)_panel != (Object)null) ? _panel.GetComponent<RectTransform>() : null);
			if ((Object)(object)val == (Object)null || (Object)(object)_column == (Object)null)
			{
				return;
			}
			float num = 76f;
			float num2 = LayoutUtility.GetPreferredHeight(_column) + num;
			float num3 = (((Component)_picking).gameObject.activeSelf ? Mathf.Max(880f, num2) : Mathf.Max(num2, 260f));
			Transform parent = ((Transform)val).parent;
			RectTransform val2 = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			if (val2 != null)
			{
				Rect rect = val2.rect;
				if (((Rect)(ref rect)).height > 0f)
				{
					float num4 = Mathf.Max(_panel.transform.localScale.y, 0.01f);
					float num5 = num3;
					rect = val2.rect;
					num3 = Mathf.Max(260f, Mathf.Min(num5, ((Rect)(ref rect)).height * 0.95f / num4));
				}
			}
			if (!(Mathf.Abs(num3 - _heightNow) < 2f))
			{
				_heightNow = num3;
				val.sizeDelta = new Vector2(780f, num3);
				if (((Component)_picking).gameObject.activeSelf)
				{
					((MonoBehaviour)this).StartCoroutine(ReportListHeight(num3));
				}
			}
		}

		private IEnumerator ReportListHeight(float frameHeight)
		{
			yield return null;
			if ((Object)(object)_list != (Object)null && (Object)(object)_picking != (Object)null && ((Component)_picking).gameObject.activeSelf)
			{
				string text = $"Innangard[stores]: sorting window {frameHeight:0} tall, hand-picked list ";
				Rect rect = _list.rect;
				Diagnostics.Log(text + $"{((Rect)(ref rect)).height:0} tall with {_choices.Count} name(s) on offer");
			}
		}

		private void Flip(StockCategories.Kind kind)
		{
			if (!((Object)(object)_container == (Object)null))
			{
				int num = 1 << (int)kind;
				_kinds = (((_kinds & num) != 0) ? (_kinds & ~num) : (_kinds | num));
				ChestSortRules.RequestKinds(_container, _kinds);
				Paint();
				Refresh();
			}
		}

		private string KindsNamed()
		{
			StringBuilder stringBuilder = new StringBuilder();
			StockCategories.Kind[] order = StockCategories.Order;
			foreach (StockCategories.Kind kind in order)
			{
				if ((_kinds & (1 << (int)kind)) != 0)
				{
					if (stringBuilder.Length > 0)
					{
						stringBuilder.Append(", ");
					}
					stringBuilder.Append(Localization.instance.Localize(StockCategories.TitleKey(kind)));
				}
			}
			return stringBuilder.ToString();
		}

		private SettlementIntake CurrentIntake()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)_container == (Object)null)
			{
				return null;
			}
			return SettlementIntake.CandidateFor(_container, localPlayer.GetPlayerID());
		}

		private void SaveLabel()
		{
			if ((Object)(object)_container == (Object)null || (Object)(object)_label == (Object)null)
			{
				return;
			}
			string text = ChestSortRules.CleanLabel(_label.text);
			if (!(text == ChestSortRules.LabelOf(_container)))
			{
				ChestSortRules.RequestLabel(_container, text);
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)2, (text.Length > 0) ? string.Format(Localization.instance.Localize("$inn_stores_label_set"), text) : Localization.instance.Localize("$inn_stores_label_cleared"), 0, (Sprite)null, false);
				}
			}
		}

		private void CopyRule()
		{
			if (!((Object)(object)_container == (Object)null))
			{
				int kinds = ChestSortRules.KindsOf(_container);
				bool configured = ChestSortRules.IsConfigured(_container);
				HashSet<string> items = ChestSortRules.ReadItems(_container);
				bool flag = ChestSortRules.IsCatchAll(_container);
				_copied = new Copied
				{
					Kinds = kinds,
					Configured = configured,
					CatchAll = flag,
					Items = items,
					Said = (flag ? Localization.instance.Localize("$inn_stores_filter_catch_all") : RuleWords(kinds, configured, items))
				};
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)2, string.Format(Localization.instance.Localize("$inn_stores_copied"), _copied.Said), 0, (Sprite)null, false);
				}
				Refresh();
			}
		}

		private void PasteRule()
		{
			if (!((Object)(object)_container == (Object)null) && _copied != null)
			{
				ApplyRule(_container, _copied.Kinds, _copied.Configured, _copied.Items, _copied.CatchAll);
				_selected = new HashSet<string>(_copied.Items, StringComparer.Ordinal);
				Populate(BuildChoices(_container, _selected));
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)2, string.Format(Localization.instance.Localize("$inn_stores_pasted"), _copied.Said), 0, (Sprite)null, false);
				}
				Refresh();
			}
		}

		internal static void ApplyRule(Container container, int kinds, bool configured, IEnumerable<string> items, bool catchAll = false)
		{
			if (catchAll)
			{
				ChestSortRules.RequestCatchAll(container);
			}
			else if (kinds != 0)
			{
				ChestSortRules.RequestKinds(container, kinds);
			}
			else
			{
				ChestSortRules.RequestSet(container, items, configured);
			}
		}

		private static string RuleWords(int kinds, bool configured, HashSet<string> items)
		{
			if (kinds != 0)
			{
				return ChestLabels.Cut(from kind in StockCategories.Order
					where (kinds & (1 << (int)kind)) != 0
					select Localization.instance.Localize(StockCategories.TitleKey(kind)));
			}
			if (!configured)
			{
				return Localization.instance.Localize("$inn_stores_filter_use_contents");
			}
			if (items.Count == 0)
			{
				return Localization.instance.Localize("$inn_stores_filter_outbox");
			}
			return ChestLabels.Cut(from name in items.Select(ChestLabels.Pretty)
				orderby name
				select name);
		}

		private void RequestIntake(IntakeRoles roles, bool said)
		{
			SettlementIntake settlementIntake = CurrentIntake();
			if (settlementIntake == null || (Object)(object)_container == (Object)null)
			{
				return;
			}
			settlementIntake.Request(_container, roles);
			if (said)
			{
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)2, "$inn_stores_intake_saved", 0, (Sprite)null, false);
				}
			}
		}

		private void Paint()
		{
			int num = 0;
			foreach (ItemChoice choice in _choices)
			{
				if (_selected.Contains(choice.SharedName))
				{
					((Component)choice.Button).transform.SetSiblingIndex(num++);
				}
			}
			foreach (ItemChoice choice2 in _choices)
			{
				bool flag = _selected.Contains(choice2.SharedName);
				InnangardGui.Chosen(choice2.Button, flag);
				if (!flag)
				{
					((Component)choice2.Button).transform.SetSiblingIndex(num++);
				}
			}
			_chosenCount.text = string.Format(Localization.instance.Localize("$inn_stores_filter_chosen"), _selected.Count);
			((MonoBehaviour)this).StartCoroutine(ScrollToTop());
		}

		private void Sort()
		{
			string looking = (((Object)(object)_search != (Object)null) ? _search.text : string.Empty).Trim();
			int num = 0;
			foreach (ItemChoice choice in _choices)
			{
				bool flag = Matches(choice, looking);
				if (((Component)choice.Button).gameObject.activeSelf != flag)
				{
					((Component)choice.Button).gameObject.SetActive(flag);
				}
				if (flag)
				{
					num++;
				}
			}
			((Component)_noMatch).gameObject.SetActive(num == 0);
		}

		private static bool Matches(ItemChoice choice, string looking)
		{
			if (string.IsNullOrWhiteSpace(looking))
			{
				return true;
			}
			if (choice.DisplayName.IndexOf(looking, StringComparison.OrdinalIgnoreCase) < 0)
			{
				return choice.SharedName.IndexOf(looking, StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return true;
		}

		private void SelectPresentMaterials()
		{
			Container container = _container;
			Inventory val = ((container != null) ? container.GetInventory() : null);
			if (val == null)
			{
				return;
			}
			foreach (ItemData allItem in val.GetAllItems())
			{
				if (ChestSortRules.IsSortable(allItem))
				{
					_selected.Add(allItem.m_shared.m_name);
				}
			}
		}

		private static List<ItemChoice> BuildChoices(Container container, HashSet<string> selected)
		{
			HashSet<string> hashSet = new HashSet<string>(selected, StringComparer.Ordinal);
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				hashSet.UnionWith(localPlayer.m_knownMaterial);
			}
			Inventory inventory = container.GetInventory();
			if (inventory != null)
			{
				hashSet.UnionWith(from item in inventory.GetAllItems()
					where item != null
					select item.m_shared.m_name);
			}
			List<ItemChoice> list = new List<ItemChoice>();
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				return list;
			}
			foreach (GameObject item in ObjectDB.instance.m_items)
			{
				ItemData val = ((item == null) ? null : item.GetComponent<ItemDrop>()?.m_itemData);
				if (ChestSortRules.IsSortable(val) && hashSet.Contains(val.m_shared.m_name))
				{
					list.Add(new ItemChoice
					{
						SharedName = val.m_shared.m_name,
						DisplayName = Localization.instance.Localize(val.m_shared.m_name),
						Icon = val.GetIcon()
					});
				}
			}
			HashSet<string> hashSet2 = new HashSet<string>(list.Select((ItemChoice choice) => choice.SharedName), StringComparer.Ordinal);
			if (inventory != null)
			{
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					if (ChestSortRules.IsSortable(allItem) && hashSet2.Add(allItem.m_shared.m_name))
					{
						list.Add(new ItemChoice
						{
							SharedName = allItem.m_shared.m_name,
							DisplayName = Localization.instance.Localize(allItem.m_shared.m_name),
							Icon = allItem.GetIcon()
						});
						Diagnostics.Log("Innangard[stores]: " + allItem.m_shared.m_name + " is in the chest but not in the item database; offered from the chest itself");
					}
				}
			}
			return (from choice in list
				group choice by choice.SharedName into @group
				select @group.First() into choice
				orderby choice.DisplayName
				select choice).ToList();
		}

		private static RectTransform Child(string name, Transform parent)
		{
			//IL_0014: 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_002e: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = new GameObject(name, new Type[1] { typeof(RectTransform) })
			{
				layer = 5
			}.GetComponent<RectTransform>();
			((Transform)component).SetParent(parent, false);
			((Transform)component).localScale = Vector3.one;
			InnangardGui.Fill(component);
			return component;
		}
	}
	internal static class ChestSortRules
	{
		[HarmonyPatch(typeof(Container), "Awake")]
		private static class RegisterPatch
		{
			[HarmonyPostfix]
			private static void Postfix(Container __instance)
			{
				Register(__instance);
			}
		}

		[HarmonyPatch(typeof(Container), "GetHoverText")]
		private static class HoverPatch
		{
			[HarmonyPostfix]
			private static void Postfix(Container __instance, ref string __result)
			{
				if ((Object)(object)Player.m_localPlayer != (Object)null && IsStationaryPlayerChest(__instance))
				{
					string text = ChestLabels.HoverLine(__instance);
					if (text.Length > 0)
					{
						__result = __result + "\n" + text;
					}
					__result = __result + "\n" + Localization.instance.Localize(InnInput.Relabel(Localization.instance.Localize("$inn_stores_filter_hover")));
				}
			}
		}

		private const char Separator = '|';

		private const string RpcSetSort = "inn_SetChestSort";

		private const string ZdoConfigured = "inn_sort_configured";

		private const string ZdoItems = "inn_sort_items";

		private const string ZdoKind = "inn_chest_sort_kind";

		private const string ZdoKinds = "inn_sort_kinds";

		private const string ZdoLeaveAlone = "inn_sort_leave_alone";

		private const string ZdoLabel = "inn_chest_label";

		private const string ZdoCatchAll = "inn_sort_catch_all";

		internal const int LabelLength = 32;

		internal const int ByHand = 0;

		internal const int ByKind = 2;

		internal const int ByLeftovers = 3;

		internal const int ByHabit = 4;

		internal const int ByCatchAll = 5;

		internal const int ByRoom = 6;

		internal const int NotAtAll = int.MaxValue;

		internal static void Register(Container container)
		{
			ZNetView val = container?.m_nview;
			if (!((Object)(object)val == (Object)null) && val.IsValid() && !(container is SettlerInventory))
			{
				val.Register<long, string>("inn_SetChestSort", (Action<long, long, string>)delegate(long sender, long requesterId, string payload)
				{
					Receive(container, sender, requesterId, payload);
				});
			}
		}

		internal static bool LeftAlone(Container container)
		{
			if (TryGetZdo(container, out var zdo))
			{
				return zdo.GetBool("inn_sort_leave_alone", false);
			}
			return false;
		}

		internal static void RequestLeaveAlone(Container container, bool leaveAlone)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && CanConfigure(container, localPlayer.GetPlayerID()))
			{
				container.m_nview.InvokeRPC("inn_SetChestSort", new object[2]
				{
					localPlayer.GetPlayerID(),
					"4|" + (leaveAlone ? "1" : "0")
				});
			}
		}

		internal static string LabelOf(Container container)
		{
			if (!TryGetZdo(container, out var zdo))
			{
				return string.Empty;
			}
			return zdo.GetString("inn_chest_label", string.Empty);
		}

		internal static string CleanLabel(string label)
		{
			if (string.IsNullOrEmpty(label))
			{
				return string.Empty;
			}
			string text = new string(label.Where((char c) => c != '<' && c != '>' && c != '|' && !char.IsControl(c)).ToArray()).Trim();
			if (text.Length <= 32)
			{
				return text;
			}
			return text.Substring(0, 32).TrimEnd(Array.Empty<char>());
		}

		internal static void RequestLabel(Container container, string label)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && CanConfigure(container, localPlayer.GetPlayerID()))
			{
				container.m_nview.InvokeRPC("inn_SetChestSort", new object[2]
				{
					localPlayer.GetPlayerID(),
					"5|" + CleanLabel(label)
				});
			}
		}

		internal static bool TogglePin(Container container, string sharedName, out bool pinned)
		{
			pinned = false;
			if ((Object)(object)container == (Object)null || string.IsNullOrEmpty(sharedName) || IsForKinds(container) || IsCatchAll(container))
			{
				return false;
			}
			HashSet<string> hashSet = (IsConfigured(container) ? ReadItems(container) : new HashSet<string>(StringComparer.Ordinal));
			pinned = !hashSet.Remove(sharedName);
			if (pinned)
			{
				hashSet.Add(sharedName);
			}
			RequestSet(container, hashSet, hashSet.Count > 0, openHere: true);
			return true;
		}

		internal static bool PinContents(Container container, out int added)
		{
			added = 0;
			Inventory val = ((container != null) ? container.GetInventory() : null);
			if (val == null || IsForKinds(container) || IsCatchAll(container))
			{
				return false;
			}
			HashSet<string> hashSet = (IsConfigured(container) ? ReadItems(container) : new HashSet<string>(StringComparer.Ordinal));
			foreach (ItemData allItem in val.GetAllItems())
			{
				if (IsSortable(allItem) && hashSet.Add(allItem.m_shared.m_name))
				{
					added++;
				}
			}
			if (hashSet.Count == 0)
			{
				return false;
			}
			RequestSet(container, hashSet, configured: true, openHere: true);
			return true;
		}

		internal static bool WasToldWhatItHolds(Container container)
		{
			if (KindsOf(container) == 0 && !IsConfigured(container))
			{
				return IsCatchAll(container);
			}
			return true;
		}

		internal static bool IsCatchAll(Container container)
		{
			if (TryGetZdo(container, out var zdo))
			{
				return zdo.GetBool("inn_sort_catch_all", false);
			}
			return false;
		}

		internal static void RequestCatchAll(Container container)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && CanConfigure(container, localPlayer.GetPlayerID()))
			{
				container.m_nview.InvokeRPC("inn_SetChestSort", new object[2]
				{
					localPlayer.GetPlayerID(),
					"6|1"
				});
			}
		}

		internal static bool Refuses(Container container, string sharedName)
		{
			if (WasToldWhatItHolds(container))
			{
				return Precision(container, sharedName) == int.MaxValue;
			}
			return false;
		}

		internal static bool IsConfigured(Container container)
		{
			if (TryGetZdo(container, out var zdo))
			{
				return zdo.GetBool("inn_sort_configured", false);
			}
			return false;
		}

		internal static int KindsOf(Container container)
		{
			if (!TryGetZdo(container, out var zdo))
			{
				return 0;
			}
			int num = zdo.GetInt("inn_sort_kinds", 0);
			if (num != 0)
			{
				return num & ((1 << StockCategories.Order.Length) - 1);
			}
			int num2 = zdo.GetInt("inn_chest_sort_kind", 0);
			if (num2 <= 0 || num2 > StockCategories.Order.Length)
			{
				return 0;
			}
			return 1 << num2 - 1;
		}

		internal static bool IsForKinds(Container container)
		{
			return KindsOf(container) != 0;
		}

		internal static bool HasKind(Container container, StockCategories.Kind kind)
		{
			return (KindsOf(container) & (1 << (int)kind)) != 0;
		}

		internal static HashSet<string> ReadItems(Container container)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			if (!TryGetZdo(container, out var zdo))
			{
				return hashSet;
			}
			string[] array = zdo.GetString("inn_sort_items", string.Empty).Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string item in array)
			{
				hashSet.Add(item);
			}
			return hashSet;
		}

		internal static bool Accepts(Container container, string sharedName)
		{
			if ((Object)(object)container == (Object)null || string.IsNullOrEmpty(sharedName))
			{
				return false;
			}
			if (IsCatchAll(container))
			{
				return true;
			}
			int num = KindsOf(container);
			if (num != 0)
			{
				return (num & (1 << (int)StockCategories.Of(sharedName))) != 0;
			}
			if (IsConfigured(container))
			{
				return ReadItems(container).Contains(sharedName);
			}
			return Dominates(container, sharedName);
		}

		internal static int Precision(Container container, string sharedName)
		{
			if ((Object)(object)container == (Object)null || string.IsNullOrEmpty(sharedName))
			{
				return int.MaxValue;
			}
			if (IsCatchAll(container))
			{
				return 5;
			}
			int num = KindsOf(container);
			if (num != 0)
			{
				StockCategories.Kind kind = StockCategories.Of(sharedName);
				if ((num & (1 << (int)kind)) == 0)
				{
					return int.MaxValue;
				}
				if (kind != StockCategories.Kind.Other)
				{
					return 2;
				}
				return 3;
			}
			if (IsConfigured(container))
			{
				if (!ReadItems(container).Contains(sharedName))
				{
					return int.MaxValue;
				}
				return 0;
			}
			if (!Dominates(container, sharedName))
			{
				return int.MaxValue;
			}
			if (!Empty(container))
			{
				return 4;
			}
			return 6;
		}

		private static bool Empty(Container container)
		{
			ChestContents.Summary summary = ChestContents.Of(container);
			if (summary != null)
			{
				return summary.UsedSortable == 0;
			}
			return true;
		}

		internal static bool Dominates(Container container, string sharedName)
		{
			return ChestContents.Of(container)?.Dominates(sharedName) ?? false;
		}

		internal static bool IsSortable(ItemData item)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Invalid comparison between Unknown and I4
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Invalid comparison between Unknown and I4
			//IL_0036: 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)
			//IL_003a: Invalid comparison between Unknown and I4
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Invalid comparison between Unknown and I4
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Invalid comparison between Unknown and I4
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Invalid comparison between Unknown and I4
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Invalid comparison between Unknown and I4
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if (item?.m_shared == null || item.m_shared.m_questItem || item.m_equipped)
			{
				return false;
			}
			ItemType itemType = item.m_shared.m_itemType;
			if ((int)itemType <= 13)
			{
				if (itemType - 1 <= 1 || (int)itemType == 9 || (int)itemType == 13)
				{
					goto IL_0057;
				}
			}
			else if ((int)itemType == 16 || (int)itemType == 21 || (int)itemType == 23)
			{
				goto IL_0057;
			}
			return StockCategories.IsEquipment(item.m_shared.m_itemType);
			IL_0057:
			return true;
		}

		internal static void RequestSet(Container container, IEnumerable<string> items, bool configured, bool openHere = false)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !CanConfigure(container, localPlayer.GetPlayerID(), openHere))
			{
				return;
			}
			string text = "0";
			if (configured)
			{
				IOrderedEnumerable<string> values = from name in items.Where((string name) => !string.IsNullOrEmpty(name) && name.IndexOf('|') < 0).Distinct()
					orderby name
					select name;
				text = "1|" + string.Join('|'.ToString(), values);
			}
			container.m_nview.InvokeRPC("inn_SetChestSort", new object[2]
			{
				localPlayer.GetPlayerID(),
				text
			});
		}

		internal static void RequestKinds(Container container, int mask)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && CanConfigure(container, localPlayer.GetPlayerID()))
			{
				container.m_nview.InvokeRPC("inn_SetChestSort", new object[2]
				{
					localPlayer.GetPlayerID(),
					"3|" + mask
				});
			}
		}

		internal static bool IsStationaryPlayerChest(Container container)
		{
			if ((Object)(object)container == (Object)null || container is SettlerInventory || !((Component)container).gameObject.activeInHierarchy)
			{
				return false;
			}
			if ((Object)(object)container.m_nview == (Object)null || !container.m_nview.IsValid())
			{
				return false;
			}
			if ((Object)(object)container.m_wagon != (Object)null || (Object)(object)((Component)container).GetComponentInParent<Ship>() != (Object)null || (Object)(object)((Component)container).GetComponentInParent<Vagon>() != (Object)null)
			{
				return false;
			}
			if (Devours(container))
			{
				return false;
			}
			Piece val = ((Component)container).GetComponent<Piece>() ?? ((Component)container).GetComponentInParent<Piece>();
			if ((Object)(object)val != (Object)null)
			{
				return val.IsPlacedByPlayer();
			}
			return false;
		}

		internal static bool Devours(Container container)
		{
			if ((Object)(object)container != (Object)null)
			{
				if (!((Object)(object)((Component)container).GetComponent<Incinerator>() != (Object)null))
				{
					return (Object)(object)((Component)container).GetComponentInParent<Incinerator>() != (Object)null;
				}
				return true;
			}
			return false;
		}

		internal static bool CanConfigure(Container container, long playerId, bool openHere = false)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			bool flag = openHere && (Object)(object)container != (Object)null && (Object)(object)InventoryGui.instance != (Object)null && (Object)(object)InventoryGui.instance.m_currentContainer == (Object)(object)container;
			if (playerId == 0L || !IsStationaryPlayerChest(container) || (!flag && InUse(container)) || !container.CheckAccess(playerId))
			{
				return false;
			}
			return PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false);
		}

		private static void Receive(Container container, long sender, long requesterId, string payload)
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_0358: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)container == (Object)null || (Object)(object)container.m_nview == (Object)null || !container.m_nview.IsValid() || reques