Decompiled source of StorageTerminal v1.0.1

plugins/StorageTerminal.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.InputSystem.UI;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("Tremendouz")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Tremendouz")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("Storage Terminal")]
[assembly: AssemblyTitle("StorageTerminal")]
[assembly: AssemblyVersion("1.0.1.0")]
[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 StorageTerminal
{
	internal static class Access
	{
		public static readonly FieldRef<Container, Inventory> ContainerInventory = AccessTools.FieldRefAccess<Container, Inventory>("m_inventory");

		public static readonly FieldRef<Inventory, List<ItemData>> Items = AccessTools.FieldRefAccess<Inventory, List<ItemData>>("m_inventory");

		public static readonly FieldRef<InventoryGui, Container> CurrentContainer = AccessTools.FieldRefAccess<InventoryGui, Container>("m_currentContainer");

		public static readonly FieldRef<InventoryGui, ItemData> DragItem = AccessTools.FieldRefAccess<InventoryGui, ItemData>("m_dragItem");

		public static readonly FieldRef<InventoryGui, InventoryGrid> ContainerGrid = AccessTools.FieldRefAccess<InventoryGui, InventoryGrid>("m_containerGrid");

		private static readonly Func<Container, long, bool> checkAccess = AccessTools.MethodDelegate<Func<Container, long, bool>>(AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null), (object)null, true);

		private static readonly Func<Inventory, ItemData, int, int, int, bool, bool> addItemAt = AccessTools.MethodDelegate<Func<Inventory, ItemData, int, int, int, bool, bool>>(AccessTools.Method(typeof(Inventory), "AddItem", new Type[5]
		{
			typeof(ItemData),
			typeof(int),
			typeof(int),
			typeof(int),
			typeof(bool)
		}, (Type[])null), (object)null, true);

		private static readonly Func<Inventory, bool, Vector2i> findEmptySlot = AccessTools.MethodDelegate<Func<Inventory, bool, Vector2i>>(AccessTools.Method(typeof(Inventory), "FindEmptySlot", new Type[1] { typeof(bool) }, (Type[])null), (object)null, true);

		private static readonly Action<Inventory, bool, bool> changed = AccessTools.MethodDelegate<Action<Inventory, bool, bool>>(AccessTools.Method(typeof(Inventory), "Changed", new Type[2]
		{
			typeof(bool),
			typeof(bool)
		}, (Type[])null), (object)null, true);

		private static readonly Func<InventoryGrid, int, int, int, InventoryElement> getElement = AccessTools.MethodDelegate<Func<InventoryGrid, int, int, int, InventoryElement>>(AccessTools.Method(typeof(InventoryGrid), "GetElement", new Type[3]
		{
			typeof(int),
			typeof(int),
			typeof(int)
		}, (Type[])null), (object)null, true);

		public static bool CheckAccess(Container c, long playerId)
		{
			return checkAccess(c, playerId);
		}

		public static InventoryElement GetElement(InventoryGrid grid, Vector2i pos, int width)
		{
			//IL_0006: 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)
			return getElement(grid, pos.x, pos.y, width);
		}

		public static bool AddItemAt(Inventory inv, ItemData item, int amount, Vector2i pos)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			return addItemAt(inv, item, amount, pos.x, pos.y, arg6: false);
		}

		public static Vector2i FindEmptySlot(Inventory inv)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			return findEmptySlot(inv, arg2: false);
		}

		public static void Changed(Inventory inv)
		{
			changed(inv, arg2: true, arg3: false);
		}
	}
	internal static class ChestBulk
	{
		private static bool _running;

		public static void TryRun()
		{
			if (_running || !Plugin.EnableAllChests.Value || (Object)(object)ZNetScene.instance == (Object)null || ZDOMan.instance == null || !Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				return;
			}
			_running = true;
			try
			{
				Run(out var enabled, out var skipped);
				Plugin.Log.LogInfo((object)$"EnableAllChests: enabled insertion/withdraw on {enabled} player-built chests, skipped {skipped} world chests.");
				((Character)Player.m_localPlayer).Message((MessageType)2, Localization.instance.Localize("$st_enabled_all", new string[1] { enabled.ToString() }), 0, (Sprite)null, false);
			}
			finally
			{
				Plugin.EnableAllChests.Value = false;
				_running = false;
			}
		}

		private static void Run(out int enabled, out int skipped)
		{
			enabled = 0;
			skipped = 0;
			List<ZDO> list = new List<ZDO>();
			foreach (GameObject prefab in ZNetScene.instance.m_prefabs)
			{
				if (!Object.op_Implicit((Object)(object)prefab) || !Object.op_Implicit((Object)(object)prefab.GetComponent<Piece>()) || Object.op_Implicit((Object)(object)prefab.GetComponent<TerminalMarker>()))
				{
					continue;
				}
				Container component = prefab.GetComponent<Container>();
				if (!Object.op_Implicit((Object)(object)component) || Object.op_Implicit((Object)(object)component.m_wagon))
				{
					continue;
				}
				list.Clear();
				int num = 0;
				while (!ZDOMan.instance.GetAllZDOsWithPrefabIterative(((Object)prefab).name, list, ref num))
				{
				}
				foreach (ZDO item in list)
				{
					if (item.GetLong(ZDOVars.s_creator, 0L) == 0L)
					{
						skipped++;
						continue;
					}
					item.Set("storageterminal_withdraw", true);
					item.Set("storageterminal_insert", true);
					enabled++;
				}
			}
		}
	}
	internal static class FilterPanel
	{
		private const int Columns = 4;

		private const float RowHeight = 30f;

		private const float Padding = 12f;

		private static GameObject _root;

		private static Toggle _all;

		private static readonly Dictionary<ItemFilter, Toggle> _toggles = new Dictionary<ItemFilter, Toggle>();

		private static bool _updating;

		public static bool IsOpen
		{
			get
			{
				if (Object.op_Implicit((Object)(object)_root))
				{
					return _root.activeSelf;
				}
				return false;
			}
		}

		public static void Toggle()
		{
			if (Object.op_Implicit((Object)(object)_root))
			{
				_root.SetActive(!_root.activeSelf);
			}
		}

		public static void Hide()
		{
			if (Object.op_Implicit((Object)(object)_root) && _root.activeSelf)
			{
				_root.SetActive(false);
			}
		}

		public static void Create(Transform parent, float width, float top)
		{
			//IL_0030: 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)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			_toggles.Clear();
			float num = (float)((ItemFilter.All.Count + 1 + 4 - 1) / 4) * 30f + 24f;
			_root = GUIManager.Instance.CreateWoodpanel(parent, Vector2.zero, Vector2.zero, Vector2.zero, width, num, false);
			((Object)_root).name = "StorageTerminal_Filters";
			RectTransform val = (RectTransform)_root.transform;
			val.pivot = new Vector2(0f, 1f);
			val.anchoredPosition = new Vector2(0f, top);
			float columnWidth = (width - 24f) / 4f;
			_all = AddCell(0, "All", columnWidth, num);
			((UnityEvent<bool>)(object)_all.onValueChanged).AddListener((UnityAction<bool>)OnAllChanged);
			int num2 = 1;
			foreach (ItemFilter item in ItemFilter.All)
			{
				Toggle val2 = AddCell(num2++, item.Label, columnWidth, num);
				ItemFilter captured = item;
				((UnityEvent<bool>)(object)val2.onValueChanged).AddListener((UnityAction<bool>)delegate(bool v)
				{
					OnFilterChanged(captured, v);
				});
				_toggles[item] = val2;
			}
			_root.SetActive(false);
		}

		public static void Sync(List<ItemFilter> selected)
		{
			if (!Object.op_Implicit((Object)(object)_root))
			{
				return;
			}
			_updating = true;
			_all.isOn = selected.Count == 0;
			foreach (KeyValuePair<ItemFilter, Toggle> toggle in _toggles)
			{
				toggle.Value.isOn = selected.Contains(toggle.Key);
			}
			_updating = false;
		}

		private static Toggle AddCell(int index, string label, float columnWidth, float panelHeight)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			GUIManager instance = GUIManager.Instance;
			int num = index % 4;
			int num2 = index / 4;
			float num3 = 12f + (float)num * columnWidth;
			float num4 = panelHeight - 12f - (float)num2 * 30f - 15f;
			GameObject obj = instance.CreateToggle(_root.transform, 24f, 24f);
			RectTransform val = (RectTransform)obj.transform;
			Vector2 anchorMin = (val.anchorMax = Vector2.zero);
			val.anchorMin = anchorMin;
			val.anchoredPosition = new Vector2(num3 + 12f, num4);
			Text component = instance.CreateText(label, _root.transform, Vector2.zero, Vector2.zero, new Vector2(num3 + 30f + (columnWidth - 34f) / 2f, num4), instance.AveriaSerifBold, 14, instance.ValheimBeige, true, Color.black, columnWidth - 34f, 30f, false).GetComponent<Text>();
			component.alignment = (TextAnchor)3;
			component.horizontalOverflow = (HorizontalWrapMode)1;
			return obj.GetComponent<Toggle>();
		}

		private static void OnAllChanged(bool on)
		{
			if (_updating)
			{
				return;
			}
			_updating = true;
			if (on)
			{
				foreach (Toggle value in _toggles.Values)
				{
					value.isOn = false;
				}
			}
			else if (!_toggles.Values.Any((Toggle t) => t.isOn))
			{
				_all.isOn = true;
			}
			_updating = false;
			Apply();
		}

		private static void OnFilterChanged(ItemFilter filter, bool on)
		{
			if (!_updating)
			{
				_updating = true;
				if (on)
				{
					_all.isOn = false;
				}
				else if (!_toggles.Values.Any((Toggle t) => t.isOn))
				{
					_all.isOn = true;
				}
				_updating = false;
				Apply();
			}
		}

		private static void Apply()
		{
			List<ItemFilter> filters = (_all.isOn ? new List<ItemFilter>() : (from kv in _toggles
				where kv.Value.isOn
				select kv.Key).ToList());
			FilterSettings.Save(filters);
			TerminalSession.Current?.SetFilters(filters);
		}
	}
	internal static class FilterSettings
	{
		public static List<ItemFilter> Load()
		{
			HashSet<string> ids = new HashSet<string>(from s in (Plugin.Filters.Value ?? "").Split(',')
				select s.Trim());
			return ItemFilter.All.Where((ItemFilter f) => ids.Contains(f.Id)).ToList();
		}

		public static void Save(IEnumerable<ItemFilter> filters)
		{
			Plugin.Filters.Value = string.Join(",", filters.Select((ItemFilter f) => f.Id));
		}
	}
	internal static class ItemCategory
	{
		public const int Weapons = 10;

		public const int Ammo = 20;

		public const int Armor = 30;

		public const int Tools = 40;

		public const int Food = 50;

		public const int Fish = 55;

		public const int Trophies = 90;

		public const int MaterialBase = 100;

		public const int Wood = 100;

		public const int Hides = 101;

		public const int Ores = 102;

		public const int Metals = 103;

		public const int Stone = 104;

		public const int Valuables = 105;

		public const int Plants = 106;

		public const int CreatureDrops = 107;

		public const int OtherMaterials = 108;

		public static int Get(ItemData item)
		{
			//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_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected I4, but got Unknown
			ItemType itemType = item.m_shared.m_itemType;
			switch (itemType - 2)
			{
			case 1:
			case 2:
			case 3:
			case 12:
			case 13:
			case 20:
				return 10;
			case 7:
			case 21:
				return 20;
			case 4:
			case 5:
			case 9:
			case 10:
			case 15:
			case 16:
			case 22:
				return 30;
			case 17:
				return 40;
			case 0:
				return 50;
			case 19:
				return 55;
			case 11:
				return 90;
			default:
				return MaterialGroup(item);
			}
		}

		private static int MaterialGroup(ItemData item)
		{
			string id = ((Object.op_Implicit((Object)(object)item.m_dropPrefab) ? ((Object)item.m_dropPrefab).name : "") + " " + item.m_shared.m_name).ToLowerInvariant();
			if (Has(id, "wood", "roundlog", "bark"))
			{
				return 100;
			}
			if (Has(id, "leather", "hide", "pelt", "scale", "chitin", "carapace"))
			{
				return 101;
			}
			if (Has(id, "ore", "scrap"))
			{
				return 102;
			}
			if (item.m_shared.m_value > 0)
			{
				return 105;
			}
			if (IsMetal(id))
			{
				return 103;
			}
			if (Has(id, "stone", "flint", "obsidian", "grausten", "marble", "crystal", "coal", "tar", "resin"))
			{
				return 104;
			}
			if (Has(id, "seed", "flax", "barley", "linen", "thistle", "dandelion", "mushroom", "carrot", "turnip", "onion", "berr", "sap", "fiddlehead", "jotunpuff", "magecap", "vineberry"))
			{
				return 106;
			}
			if (Has(id, "feather", "bone", "tooth", "fang", "claw", "horn", "antler", "eye", "heart", "entrails", "ichor", "bile", "ooze", "guck", "wisp", "core", "head"))
			{
				return 107;
			}
			return 108;
		}

		private static bool IsMetal(string id)
		{
			if (!Has(id, "copper", "bronze", "iron", "silver", "blackmetal", "flametal", "nails"))
			{
				return id.StartsWith("tin ");
			}
			return true;
		}

		private static bool Has(string id, params string[] parts)
		{
			foreach (string value in parts)
			{
				if (id.Contains(value))
				{
					return true;
				}
			}
			return false;
		}
	}
	internal sealed class ItemFilter
	{
		public readonly string Id;

		public readonly string Label;

		private readonly int _min;

		private readonly int _max;

		public static readonly IReadOnlyList<ItemFilter> All = new ItemFilter[17]
		{
			new ItemFilter("materials", "Materials (all)", 100, int.MaxValue),
			new ItemFilter("wood", "Wood", 100),
			new ItemFilter("stone", "Stone & minerals", 104),
			new ItemFilter("ores", "Ores", 102),
			new ItemFilter("metals", "Metals", 103),
			new ItemFilter("hides", "Hides & leather", 101),
			new ItemFilter("plants", "Plants & seeds", 106),
			new ItemFilter("drops", "Creature drops", 107),
			new ItemFilter("valuables", "Valuables", 105),
			new ItemFilter("food", "Food & potions", 50),
			new ItemFilter("ammo", "Ammo", 20),
			new ItemFilter("weapons", "Weapons", 10),
			new ItemFilter("armor", "Armor", 30),
			new ItemFilter("tools", "Tools", 40),
			new ItemFilter("fish", "Fish", 55),
			new ItemFilter("trophies", "Trophies", 90),
			new ItemFilter("othermaterials", "Other materials", 108)
		};

		private ItemFilter(string id, string label, int category)
			: this(id, label, category, category)
		{
		}

		private ItemFilter(string id, string label, int min, int max)
		{
			Id = id;
			Label = label;
			_min = min;
			_max = max;
		}

		public bool Matches(int category)
		{
			if (category >= _min)
			{
				return category <= _max;
			}
			return false;
		}
	}
	internal static class LinkedChests
	{
		private static readonly List<Piece> pieces = new List<Piece>();

		public static List<Container> Find(Vector3 center, Func<Container, bool> flag)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			List<Container> list = new List<Container>();
			if ((Object)(object)Game.instance == (Object)null)
			{
				return list;
			}
			long playerID = Game.instance.GetPlayerProfile().GetPlayerID();
			pieces.Clear();
			Piece.GetAllPiecesInRadius(center, Plugin.Range.Value, pieces);
			foreach (Piece piece in pieces)
			{
				Container val = (Object.op_Implicit((Object)(object)piece) ? ((Component)piece).GetComponent<Container>() : null);
				if (IsLinkable(val) && Access.CheckAccess(val, playerID) && flag(val))
				{
					list.Add(val);
				}
			}
			pieces.Clear();
			list.Sort(delegate(Container a, Container b)
			{
				//IL_0006: 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_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_0027: 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_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val2 = ((Component)a).transform.position - center;
				float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
				val2 = ((Component)b).transform.position - center;
				return sqrMagnitude.CompareTo(((Vector3)(ref val2)).sqrMagnitude);
			});
			return list;
		}

		public static bool IsLinkable(Container c)
		{
			if (!Object.op_Implicit((Object)(object)c) || Object.op_Implicit((Object)(object)c.m_wagon) || TerminalPiece.IsTerminal(c))
			{
				return false;
			}
			if (Object.op_Implicit((Object)(object)((Component)c).GetComponentInParent<Ship>()))
			{
				return false;
			}
			ZNetView component = ((Component)c).GetComponent<ZNetView>();
			if (Object.op_Implicit((Object)(object)component) && component.IsValid())
			{
				return Access.ContainerInventory.Invoke(c) != null;
			}
			return false;
		}
	}
	internal static class LinkFlags
	{
		public const string TakeKey = "storageterminal_withdraw";

		public const string InsertKey = "storageterminal_insert";

		public static bool CanTake(Container c)
		{
			return Get(c, "storageterminal_withdraw");
		}

		public static bool CanInsert(Container c)
		{
			return Get(c, "storageterminal_insert");
		}

		public static void SetTake(Container c, bool value)
		{
			Set(c, "storageterminal_withdraw", value);
		}

		public static void SetInsert(Container c, bool value)
		{
			Set(c, "storageterminal_insert", value);
		}

		private static bool Get(Container c, string key)
		{
			ZNetView val = (Object.op_Implicit((Object)(object)c) ? ((Component)c).GetComponent<ZNetView>() : null);
			if (Object.op_Implicit((Object)(object)val) && val.IsValid())
			{
				return val.GetZDO().GetBool(key, false);
			}
			return false;
		}

		private static void Set(Container c, string key, bool value)
		{
			ZNetView val = (Object.op_Implicit((Object)(object)c) ? ((Component)c).GetComponent<ZNetView>() : null);
			if (Object.op_Implicit((Object)(object)val) && val.IsValid())
			{
				if (!val.IsOwner())
				{
					val.ClaimOwnership();
				}
				val.GetZDO().Set(key, value);
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Show")]
	internal static class InventoryGuiShowPatch
	{
		private static void Prefix(Container container)
		{
			try
			{
				TerminalSession current = TerminalSession.Current;
				if (current != null && (Object)(object)current.Terminal != (Object)(object)container)
				{
					current.Close();
				}
				if (Object.op_Implicit((Object)(object)container) && TerminalPiece.IsTerminal(container) && TerminalSession.Current == null)
				{
					TerminalSession.Open(container);
					TerminalUi.OnTerminalOpened();
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Opening the terminal failed: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "CloseContainer")]
	internal static class InventoryGuiCloseContainerPatch
	{
		private static void Prefix()
		{
			TerminalSession.Current?.Close();
			TerminalUi.ReleaseInput();
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Hide")]
	internal static class InventoryGuiHidePatch
	{
		private static void Prefix()
		{
			TerminalSession.Current?.Close();
			TerminalUi.ReleaseInput();
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Update")]
	internal static class InventoryGuiUpdatePatch
	{
		private static bool _errorLogged;

		private static void Postfix(InventoryGui __instance)
		{
			try
			{
				TerminalSession.Current?.Tick(__instance);
				TerminalUi.Update(__instance);
			}
			catch (Exception arg)
			{
				if (!_errorLogged)
				{
					Plugin.Log.LogError((object)$"InventoryGui.Update: {arg}");
				}
				_errorLogged = true;
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "DropItem")]
	internal static class InventoryGridDropItemPatch
	{
		private static bool Prefix(InventoryGrid __instance, Inventory fromInventory, ItemData item, int amount, Vector2i pos, ref bool __result)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			TerminalSession current = TerminalSession.Current;
			if (current == null)
			{
				return true;
			}
			Inventory inventory = __instance.GetInventory();
			bool flag = TerminalSession.IsView(inventory);
			bool flag2 = TerminalSession.IsView(fromInventory);
			if (!flag && !flag2)
			{
				return true;
			}
			if (flag && flag2)
			{
				__result = false;
			}
			else if (flag)
			{
				__result = current.StoreFrom(fromInventory, item, amount) > 0;
			}
			else
			{
				__result = current.TakeTo(item, amount, inventory, pos) > 0;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Inventory), "MoveItemToThis", new Type[]
	{
		typeof(Inventory),
		typeof(ItemData)
	})]
	internal static class InventoryQuickMovePatch
	{
		private static bool Prefix(Inventory __instance, Inventory fromInventory, ItemData item)
		{
			TerminalSession current = TerminalSession.Current;
			if (current == null || item == null)
			{
				return true;
			}
			bool flag = TerminalSession.IsView(__instance);
			bool flag2 = TerminalSession.IsView(fromInventory);
			if (flag == flag2)
			{
				return !flag;
			}
			if (flag)
			{
				current.StoreFrom(fromInventory, item, item.m_stack);
			}
			else
			{
				current.TakeTo(item, item.m_stack, __instance, null);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Inventory), "MoveItemToThis", new Type[]
	{
		typeof(Inventory),
		typeof(ItemData),
		typeof(int),
		typeof(int),
		typeof(int)
	})]
	internal static class InventoryMoveToPosPatch
	{
		private static bool Prefix(Inventory __instance, Inventory fromInventory, ItemData item, int amount, int x, int y, ref bool __result)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			TerminalSession current = TerminalSession.Current;
			if (current == null || item == null)
			{
				return true;
			}
			bool flag = TerminalSession.IsView(__instance);
			bool flag2 = TerminalSession.IsView(fromInventory);
			if (!flag && !flag2)
			{
				return true;
			}
			if (flag && flag2)
			{
				__result = false;
			}
			else if (flag)
			{
				__result = current.StoreFrom(fromInventory, item, amount) > 0;
			}
			else
			{
				__result = current.TakeTo(item, amount, __instance, (Vector2i?)new Vector2i(x, y)) > 0;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Inventory), "StackAll")]
	internal static class InventoryStackAllPatch
	{
		private static bool Prefix(Inventory __instance, Inventory fromInventory, ref int __result)
		{
			if (!TerminalSession.IsView(__instance))
			{
				return true;
			}
			__result = TerminalSession.Current.StackAllFrom(fromInventory);
			return false;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "SetupDragItem")]
	internal static class InventoryGuiSetupDragPatch
	{
		private static void Prefix(ItemData item, Inventory inventory, ref int amount)
		{
			if (item != null && TerminalSession.IsView(inventory))
			{
				amount = Math.Min(amount, Math.Max(1, item.m_shared.m_maxStackSize));
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "DropItem")]
	internal static class HumanoidDropItemPatch
	{
		private static void Prefix(Inventory inventory, ItemData item, ref int amount)
		{
			if (item != null && TerminalSession.IsView(inventory))
			{
				amount = Math.Min(amount, Math.Max(1, item.m_shared.m_maxStackSize));
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "UpdateGui")]
	internal static class InventoryGridUpdateGuiPatch
	{
		private static void Postfix(InventoryGrid __instance)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			Inventory inventory = __instance.GetInventory();
			if (!TerminalSession.IsView(inventory))
			{
				return;
			}
			int width = inventory.GetWidth();
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem.m_shared.m_maxStackSize > 1)
				{
					InventoryElement element = Access.GetElement(__instance, allItem.m_gridPos, width);
					if ((Object)(object)element != (Object)null && Object.op_Implicit((Object)(object)element.m_amount))
					{
						element.m_amount.text = Format(allItem.m_stack);
					}
				}
			}
		}

		private static string Format(int n)
		{
			if (n >= 10000)
			{
				if (n >= 1000000)
				{
					return n / 1000000 + "M";
				}
				return n / 1000 + "k";
			}
			return n.ToString();
		}
	}
	[HarmonyPatch(typeof(Container), "Save")]
	internal static class ContainerSavePatch
	{
		private static bool Prefix(Container __instance)
		{
			return !TerminalPiece.IsTerminal(__instance);
		}
	}
	[HarmonyPatch(typeof(Container), "Load")]
	internal static class ContainerLoadPatch
	{
		private static bool Prefix(Container __instance)
		{
			return !TerminalPiece.IsTerminal(__instance);
		}
	}
	[HarmonyPatch(typeof(Container), "GetHoverText")]
	internal static class ContainerHoverPatch
	{
		private static void Postfix(Container __instance, ref string __result)
		{
			if (TerminalPiece.IsTerminal(__instance) && Localization.instance != null)
			{
				__result = __result.Replace(Localization.instance.Localize(" ( $piece_container_empty )"), "");
			}
		}
	}
	[HarmonyPatch(typeof(Piece), "SetCreator")]
	internal static class PieceSetCreatorPatch
	{
		private static void Postfix(Piece __instance)
		{
			Container component = ((Component)__instance).GetComponent<Container>();
			if (LinkedChests.IsLinkable(component))
			{
				if (Plugin.DefaultTake.Value)
				{
					LinkFlags.SetTake(component, value: true);
				}
				if (Plugin.DefaultInsert.Value)
				{
					LinkFlags.SetInsert(component, value: true);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	internal static class PlayerOnSpawnedPatch
	{
		private static void Postfix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				ChestBulk.TryRun();
			}
		}
	}
	[BepInPlugin("tremendouz.storageterminal", "Storage Terminal by Tremendouz", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	internal sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "tremendouz.storageterminal";

		public const string ModName = "Storage Terminal by Tremendouz";

		public const string Version = "1.0.1";

		internal static ManualLogSource Log;

		internal static ConfigEntry<float> Range;

		internal static ConfigEntry<string> Recipe;

		internal static ConfigEntry<bool> DefaultTake;

		internal static ConfigEntry<bool> DefaultInsert;

		internal static ConfigEntry<bool> EnableAllChests;

		internal static ConfigEntry<SortMode> Sort;

		internal static ConfigEntry<string> Filters;

		internal static ConfigEntry<bool> IgnoreWeight;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Range = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Range", 20f, new ConfigDescription("Terminal range in meters — chests within this radius are linked.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 100f), Array.Empty<object>()));
			Recipe = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Recipe", "FineWood:10,Iron:2", "Terminal build cost: Prefab:Amount, comma separated. Takes effect after a game restart.");
			DefaultTake = ((BaseUnityPlugin)this).Config.Bind<bool>("Chests", "DefaultTake", false, "Newly built chests have terminal withdraw enabled.");
			DefaultInsert = ((BaseUnityPlugin)this).Config.Bind<bool>("Chests", "DefaultInsert", false, "Newly built chests have terminal insertion enabled.");
			EnableAllChests = ((BaseUnityPlugin)this).Config.Bind<bool>("Chests", "EnableAllChests", false, "Action: set to true to enable insertion and withdraw on every player-built chest in the current world. Resets itself to false afterwards. If no world is loaded, it runs after you enter one.");
			EnableAllChests.SettingChanged += delegate
			{
				ChestBulk.TryRun();
			};
			Sort = ((BaseUnityPlugin)this).Config.Bind<SortMode>("UI", "Sort", SortMode.Name, "Last selected sort order in the terminal.");
			Filters = ((BaseUnityPlugin)this).Config.Bind<string>("UI", "Filters", "", "Last selected terminal filters (comma separated ids, empty = All).");
			IgnoreWeight = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "IgnoreWeight", false, "Recipe panel: Take also moves materials beyond the carry weight limit (checkbox in the panel).");
			Texts.Register();
			TerminalPiece.Register(Recipe.Value);
			_harmony = new Harmony("tremendouz.storageterminal");
			_harmony.PatchAll(typeof(Plugin).Assembly);
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class RecipePanel
	{
		private sealed class Craftable
		{
			public string Name;

			public string Search;

			public string Where;

			public int Output;

			public Requirement[] Resources;
		}

		private sealed class Tile
		{
			public Requirement Requirement;

			public Text Amount;
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction<string> <0>__FillList;

			public static UnityAction <1>__TakeMaterials;
		}

		private const float Width = 720f;

		private const float Height = 460f;

		private const float Column = 330f;

		private const int MaxRows = 150;

		private const float RowHeight = 28f;

		private const float RowSpacing = 2f;

		private const float TileWidth = 78f;

		private const float TileHeight = 86f;

		private const float TileSpacing = 6f;

		private const int TilesPerRow = 4;

		private const int TileRowsVisible = 3;

		private static GameObject _root;

		private static InputField _search;

		private static InputField _amount;

		private static Toggle _ignoreWeight;

		private static RectTransform _list;

		private static ScrollRect _listScroll;

		private static RectTransform _tiles;

		private static ScrollRect _tilesScroll;

		private static Text _title;

		private static readonly List<Tile> _tileViews = new List<Tile>();

		private static List<Craftable> _all = new List<Craftable>();

		private static Craftable _selected;

		public static bool IsFocused
		{
			get
			{
				if (!Object.op_Implicit((Object)(object)_search) || !_search.isFocused)
				{
					if (Object.op_Implicit((Object)(object)_amount))
					{
						return _amount.isFocused;
					}
					return false;
				}
				return true;
			}
		}

		public static void Destroy()
		{
			if (Object.op_Implicit((Object)(object)_root))
			{
				Object.Destroy((Object)(object)_root);
			}
			_root = null;
			_selected = null;
			_tileViews.Clear();
			ScrollStep.Unregister(_listScroll);
			ScrollStep.Unregister(_tilesScroll);
			_listScroll = null;
			_tilesScroll = null;
		}

		public static void SetActive(bool active)
		{
			if (Object.op_Implicit((Object)(object)_root) && _root.activeSelf != active)
			{
				_root.SetActive(active);
			}
		}

		public static void OnTerminalOpened()
		{
			if (Object.op_Implicit((Object)(object)_root))
			{
				_all = Collect();
				_search.SetTextWithoutNotify("");
				_amount.SetTextWithoutNotify("1");
				_ignoreWeight.SetIsOnWithoutNotify(Plugin.IgnoreWeight.Value);
				FillList("");
				Select(null);
			}
		}

		public static void Create(Transform fallbackParent)
		{
			try
			{
				Build(fallbackParent);
			}
			catch
			{
				Destroy();
				throw;
			}
		}

		private static void Build(Transform fallbackParent)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: 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_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Expected O, but got Unknown
			//IL_0307: Unknown result type (might be due to invalid IL or missing references)
			//IL_0308: Unknown result type (might be due to invalid IL or missing references)
			//IL_0310: Unknown result type (might be due to invalid IL or missing references)
			//IL_031e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0324: Unknown result type (might be due to invalid IL or missing references)
			//IL_036d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0372: Unknown result type (might be due to invalid IL or missing references)
			//IL_0373: Unknown result type (might be due to invalid IL or missing references)
			//IL_0374: Unknown result type (might be due to invalid IL or missing references)
			//IL_0376: Unknown result type (might be due to invalid IL or missing references)
			//IL_0377: Unknown result type (might be due to invalid IL or missing references)
			//IL_037e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0392: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0404: Unknown result type (might be due to invalid IL or missing references)
			//IL_040a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0431: Unknown result type (might be due to invalid IL or missing references)
			//IL_0433: Unknown result type (might be due to invalid IL or missing references)
			//IL_0442: Unknown result type (might be due to invalid IL or missing references)
			//IL_0450: Unknown result type (might be due to invalid IL or missing references)
			//IL_0456: Unknown result type (might be due to invalid IL or missing references)
			//IL_046e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0470: Unknown result type (might be due to invalid IL or missing references)
			//IL_047f: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_050a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0538: Unknown result type (might be due to invalid IL or missing references)
			//IL_053d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0543: Expected O, but got Unknown
			GUIManager instance = GUIManager.Instance;
			Transform val = (Object.op_Implicit((Object)(object)GUIManager.CustomGUIFront) ? GUIManager.CustomGUIFront.transform : fallbackParent);
			_root = instance.CreateWoodpanel(val, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), Vector2.zero, 720f, 460f, false);
			((Object)_root).name = "StorageTerminal_Recipes";
			RectTransform val2 = (RectTransform)_root.transform;
			val2.pivot = new Vector2(0.5f, 1f);
			val2.anchoredPosition = new Vector2(0f, -10f);
			_root.SetActive(false);
			Transform transform = _root.transform;
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0.5f, 1f);
			Vector2 val4 = default(Vector2);
			((Vector2)(ref val4))..ctor(0.5f, 0f);
			float num = -180f;
			float num2 = 180f;
			float num3 = num2 - 165f;
			instance.CreateText(Localization.instance.Localize("$st_recipes"), transform, val3, val3, new Vector2(0f, -22f), instance.AveriaSerifBold, 20, instance.ValheimOrange, true, Color.black, 300f, 30f, false).GetComponent<Text>().alignment = (TextAnchor)4;
			_search = instance.CreateInputField(transform, val3, val3, new Vector2(num, -55f), (ContentType)0, Localization.instance.Localize("$st_recipe_search"), 16, 330f, 30f).GetComponent<InputField>();
			((UnityEvent<string>)(object)_search.onValueChanged).AddListener((UnityAction<string>)FillList);
			GameObject obj = instance.CreateScrollView(transform, false, true, 8f, 5f, instance.ValheimScrollbarHandleColorBlock, new Color(0f, 0f, 0f, 0.35f), 330f, 360f);
			RectTransform val5 = (RectTransform)obj.transform;
			Vector2 anchorMin = (val5.anchorMax = val3);
			val5.anchorMin = anchorMin;
			val5.pivot = new Vector2(0.5f, 1f);
			val5.anchoredPosition = new Vector2(num, -78f);
			ScrollRect componentInChildren = obj.GetComponentInChildren<ScrollRect>(true);
			if (!Object.op_Implicit((Object)(object)componentInChildren) || !Object.op_Implicit((Object)(object)componentInChildren.content))
			{
				throw new InvalidOperationException("ScrollView without ScrollRect.content");
			}
			_listScroll = componentInChildren;
			ScrollStep.Register(componentInChildren, 30f);
			_list = componentInChildren.content;
			VerticalLayoutGroup val7 = ((Component)_list).GetComponent<VerticalLayoutGroup>();
			if (!Object.op_Implicit((Object)(object)val7))
			{
				val7 = ((Component)_list).gameObject.AddComponent<VerticalLayoutGroup>();
			}
			((HorizontalOrVerticalLayoutGroup)val7).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)val7).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)val7).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)val7).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)val7).spacing = 2f;
			((LayoutGroup)val7).padding = new RectOffset(4, 14, 4, 4);
			ContentSizeFitter val8 = ((Component)_list).GetComponent<ContentSizeFitter>();
			if (!Object.op_Implicit((Object)(object)val8))
			{
				val8 = ((Component)_list).gameObject.AddComponent<ContentSizeFitter>();
			}
			val8.verticalFit = (FitMode)2;
			_title = instance.CreateText("", transform, val3, val3, new Vector2(num2, -55f), instance.AveriaSerifBold, 17, instance.ValheimOrange, true, Color.black, 330f, 30f, false).GetComponent<Text>();
			_title.alignment = (TextAnchor)3;
			BuildTilesArea(transform, num2);
			GameObject obj2 = instance.CreateToggle(transform, 26f, 26f);
			RectTransform val9 = (RectTransform)obj2.transform;
			anchorMin = (val9.anchorMax = val4);
			val9.anchorMin = anchorMin;
			val9.anchoredPosition = new Vector2(num3 + 13f, 72f);
			_ignoreWeight = obj2.GetComponent<Toggle>();
			((UnityEvent<bool>)(object)_ignoreWeight.onValueChanged).AddListener((UnityAction<bool>)delegate(bool v)
			{
				Plugin.IgnoreWeight.Value = v;
			});
			instance.CreateText(Localization.instance.Localize("$st_ignore_weight"), transform, val4, val4, new Vector2(num3 + 140f, 72f), instance.AveriaSerifBold, 15, instance.ValheimOrange, true, Color.black, 220f, 28f, false);
			instance.CreateText(Localization.instance.Localize("$st_amount"), transform, val4, val4, new Vector2(num3 + 40f, 32f), instance.AveriaSerifBold, 16, instance.ValheimOrange, true, Color.black, 80f, 30f, false);
			_amount = instance.CreateInputField(transform, val4, val4, new Vector2(num3 + 115f, 32f), (ContentType)2, "1", 16, 70f, 30f).GetComponent<InputField>();
			_amount.text = "1";
			((UnityEvent<string>)(object)_amount.onValueChanged).AddListener((UnityAction<string>)delegate
			{
				UpdateAmounts();
			});
			ButtonClickedEvent onClick = instance.CreateButton(Localization.instance.Localize("$st_take_button"), transform, val4, val4, new Vector2(num2 + 165f - 65f, 32f), 130f, 34f).GetComponent<Button>().onClick;
			object obj3 = <>O.<1>__TakeMaterials;
			if (obj3 == null)
			{
				UnityAction val11 = TakeMaterials;
				<>O.<1>__TakeMaterials = val11;
				obj3 = (object)val11;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj3);
		}

		private static void BuildTilesArea(Transform parent, float centerX)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Expected O, but got Unknown
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0.5f, 1f);
			GameObject val2 = new GameObject("TilesViewport", new Type[3]
			{
				typeof(RectTransform),
				typeof(RectMask2D),
				typeof(Image)
			});
			RectTransform val3 = (RectTransform)val2.transform;
			((Transform)val3).SetParent(parent, false);
			Vector2 anchorMin = (val3.anchorMax = val);
			val3.anchorMin = anchorMin;
			val3.pivot = new Vector2(0.5f, 1f);
			val3.sizeDelta = new Vector2(330f, 270f);
			val3.anchoredPosition = new Vector2(centerX, -78f);
			((Graphic)val2.GetComponent<Image>()).color = Color.clear;
			GameObject val5 = new GameObject("Tiles", new Type[1] { typeof(RectTransform) });
			_tiles = (RectTransform)val5.transform;
			((Transform)_tiles).SetParent((Transform)(object)val3, false);
			_tiles.anchorMin = new Vector2(0f, 1f);
			_tiles.anchorMax = new Vector2(1f, 1f);
			_tiles.pivot = new Vector2(0.5f, 1f);
			_tiles.sizeDelta = Vector2.zero;
			GridLayoutGroup obj = val5.AddComponent<GridLayoutGroup>();
			obj.cellSize = new Vector2(78f, 86f);
			obj.spacing = new Vector2(6f, 6f);
			obj.constraint = (Constraint)1;
			obj.constraintCount = 4;
			val5.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2;
			_tilesScroll = val2.AddComponent<ScrollRect>();
			_tilesScroll.horizontal = false;
			_tilesScroll.vertical = true;
			_tilesScroll.movementType = (MovementType)2;
			_tilesScroll.viewport = val3;
			_tilesScroll.content = _tiles;
			ScrollStep.Register(_tilesScroll, 92f);
		}

		private static List<Craftable> Collect()
		{
			List<Craftable> list = new List<Craftable>();
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer) || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return list;
			}
			HashSet<string> hashSet = new HashSet<string>();
			foreach (Recipe recipe in ObjectDB.instance.m_recipes)
			{
				if (Object.op_Implicit((Object)(object)recipe) && recipe.m_enabled && Object.op_Implicit((Object)(object)recipe.m_item) && recipe.m_resources != null && recipe.m_resources.Length != 0)
				{
					SharedData shared = recipe.m_item.m_itemData.m_shared;
					if (localPlayer.IsRecipeKnown(shared.m_name) && hashSet.Add("R" + shared.m_name))
					{
						list.Add(Make(shared.m_name, StationTag(recipe.m_craftingStation), recipe.m_amount, recipe.m_resources));
					}
				}
			}
			foreach (GameObject item in ObjectDB.instance.m_items)
			{
				ItemDrop val = (Object.op_Implicit((Object)(object)item) ? item.GetComponent<ItemDrop>() : null);
				PieceTable val2 = (Object.op_Implicit((Object)(object)val) ? val.m_itemData.m_shared.m_buildPieces : null);
				if (!Object.op_Implicit((Object)(object)val2))
				{
					continue;
				}
				foreach (GameObject piece in val2.m_pieces)
				{
					Piece val3 = (Object.op_Implicit((Object)(object)piece) ? piece.GetComponent<Piece>() : null);
					if (Object.op_Implicit((Object)(object)val3) && val3.m_enabled && val3.m_resources != null && val3.m_resources.Length != 0 && localPlayer.IsRecipeKnown(val3.m_name) && hashSet.Add("P" + val3.m_name))
					{
						list.Add(Make(val3.m_name, "build", 1, val3.m_resources));
					}
				}
			}
			list.Sort((Craftable a, Craftable b) => string.Compare(a.Name, b.Name, StringComparison.CurrentCultureIgnoreCase));
			Plugin.Log.LogDebug((object)$"Recipes: {list.Count} known.");
			return list;
		}

		private static string StationTag(CraftingStation station)
		{
			if (!Object.op_Implicit((Object)(object)station))
			{
				return "craft";
			}
			return station.m_name switch
			{
				"$piece_workbench" => "workbench", 
				"$piece_forge" => "forge", 
				"$piece_cauldron" => "cauldron", 
				"$piece_stonecutter" => "stonecutter", 
				"$piece_artisanstation" => "artisan", 
				"$piece_blackforge" => "black forge", 
				"$piece_magetable" => "galdr", 
				"$piece_preptable" => "prep table", 
				"$piece_meadcauldron" => "mead ketill", 
				_ => Localization.instance.Localize(station.m_name).ToLowerInvariant(), 
			};
		}

		private static Craftable Make(string token, string where, int output, Requirement[] resources)
		{
			string text = Localization.instance.Localize(token);
			return new Craftable
			{
				Name = text,
				Search = TerminalSession.Normalize(text),
				Where = where,
				Output = Math.Max(1, output),
				Resources = resources.Where((Requirement r) => r != null && Object.op_Implicit((Object)(object)r.m_resItem)).ToArray()
			};
		}

		private static void FillList(string filter)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Expected O, but got Unknown
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)_list))
			{
				return;
			}
			foreach (Transform item in (Transform)_list)
			{
				Object.Destroy((Object)(object)((Component)item).gameObject);
			}
			string needle = TerminalSession.Normalize(filter ?? "");
			foreach (Craftable item2 in _all.Where((Craftable c) => needle.Length == 0 || c.Search.Contains(needle)).Take(150))
			{
				string text = ((item2.Output > 1) ? $"{item2.Name} ×{item2.Output}" : item2.Name) + "  <color=#a0a0a0>(" + item2.Where + ")</color>";
				GameObject obj = GUIManager.Instance.CreateButton(text, (Transform)(object)_list, Vector2.zero, Vector2.zero, Vector2.zero, 300f, 28f);
				Text componentInChildren = obj.GetComponentInChildren<Text>();
				if (Object.op_Implicit((Object)(object)componentInChildren))
				{
					componentInChildren.supportRichText = true;
					componentInChildren.fontSize = 15;
					componentInChildren.alignment = (TextAnchor)3;
					RectTransform rectTransform = ((Graphic)componentInChildren).rectTransform;
					rectTransform.anchorMin = Vector2.zero;
					rectTransform.anchorMax = Vector2.one;
					rectTransform.offsetMin = new Vector2(10f, 0f);
					rectTransform.offsetMax = new Vector2(-6f, 0f);
				}
				LayoutElement obj2 = obj.AddComponent<LayoutElement>();
				obj2.preferredHeight = 28f;
				obj2.minHeight = 28f;
				Craftable captured = item2;
				((UnityEvent)obj.GetComponent<Button>().onClick).AddListener((UnityAction)delegate
				{
					Select(captured);
				});
			}
		}

		private static void Select(Craftable craftable)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			_selected = craftable;
			foreach (Transform item in (Transform)_tiles)
			{
				Object.Destroy((Object)(object)((Component)item).gameObject);
			}
			_tileViews.Clear();
			if (craftable == null)
			{
				_title.text = Localization.instance.Localize("$st_select_recipe");
				return;
			}
			_title.text = ((craftable.Output > 1) ? $"{craftable.Name} ×{craftable.Output}" : craftable.Name);
			Requirement[] resources = craftable.Resources;
			foreach (Requirement req in resources)
			{
				_tileViews.Add(CreateTile(req));
			}
			if (Object.op_Implicit((Object)(object)_tilesScroll))
			{
				_tilesScroll.verticalNormalizedPosition = 1f;
			}
			UpdateAmounts();
		}

		private static Tile CreateTile(Requirement req)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			//IL_005c: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: 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_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: 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_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			GUIManager instance = GUIManager.Instance;
			GameObject val = new GameObject("Tile", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			val.transform.SetParent((Transform)(object)_tiles, false);
			((Graphic)val.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.45f);
			GameObject val2 = new GameObject("Icon", new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			});
			RectTransform val3 = (RectTransform)val2.transform;
			((Transform)val3).SetParent(val.transform, false);
			Vector2 val4 = default(Vector2);
			((Vector2)(ref val4))..ctor(0.5f, 1f);
			val3.anchorMax = val4;
			val3.anchorMin = val4;
			val3.pivot = new Vector2(0.5f, 1f);
			val3.sizeDelta = new Vector2(40f, 40f);
			val3.anchoredPosition = new Vector2(0f, -4f);
			Image component = val2.GetComponent<Image>();
			component.sprite = req.m_resItem.m_itemData.GetIcon();
			component.preserveAspect = true;
			Text component2 = instance.CreateText(Localization.instance.Localize(req.m_resItem.m_itemData.m_shared.m_name), val.transform, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 30f), instance.AveriaSerif, 11, instance.ValheimBeige, true, Color.black, 74f, 16f, false).GetComponent<Text>();
			component2.alignment = (TextAnchor)4;
			component2.horizontalOverflow = (HorizontalWrapMode)0;
			component2.resizeTextForBestFit = true;
			component2.resizeTextMinSize = 8;
			component2.resizeTextMaxSize = 11;
			Text component3 = instance.CreateText("", val.transform, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 12f), instance.AveriaSerifBold, 13, instance.ValheimBeige, true, Color.black, 74f, 18f, false).GetComponent<Text>();
			component3.alignment = (TextAnchor)4;
			component3.supportRichText = true;
			return new Tile
			{
				Requirement = req,
				Amount = component3
			};
		}

		private static void UpdateAmounts()
		{
			if (_selected == null)
			{
				return;
			}
			TerminalSession current = TerminalSession.Current;
			Inventory val = (Object.op_Implicit((Object)(object)Player.m_localPlayer) ? ((Humanoid)Player.m_localPlayer).GetInventory() : null);
			int crafts = Crafts();
			foreach (Tile tileView in _tileViews)
			{
				string name = tileView.Requirement.m_resItem.m_itemData.m_shared.m_name;
				int num = Need(tileView.Requirement, crafts);
				int num2 = current?.CountAvailable(name) ?? 0;
				int num3 = ((val != null) ? val.CountItems(name, -1, true) : 0);
				bool flag = num2 + num3 >= num;
				tileView.Amount.text = (flag ? $"{num}/{num2}" : $"<color=#ff6060>{num}/{num2}</color>");
			}
		}

		private static int Crafts()
		{
			if (!int.TryParse(Object.op_Implicit((Object)(object)_amount) ? _amount.text : "1", out var result))
			{
				return 1;
			}
			return Mathf.Clamp(result, 1, 999);
		}

		private static int Need(Requirement req, int crafts)
		{
			if (!Object.op_Implicit((Object)(object)req.m_resItem))
			{
				return 0;
			}
			return req.GetAmount(1) * crafts;
		}

		private static void TakeMaterials()
		{
			TerminalSession session = TerminalSession.Current;
			Player localPlayer = Player.m_localPlayer;
			if (session == null || !Object.op_Implicit((Object)(object)localPlayer) || _selected == null)
			{
				return;
			}
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			int crafts = Crafts();
			List<KeyValuePair<SharedData, int>> list = new List<KeyValuePair<SharedData, int>>();
			Requirement[] resources = _selected.Resources;
			foreach (Requirement val in resources)
			{
				int num = Need(val, crafts);
				if (num > 0)
				{
					SharedData shared = val.m_resItem.m_itemData.m_shared;
					int num2 = num - inventory.CountItems(shared.m_name, -1, true);
					if (num2 > 0)
					{
						list.Add(new KeyValuePair<SharedData, int>(shared, num2));
					}
				}
			}
			if (list.Count == 0)
			{
				((Character)localPlayer).Message((MessageType)2, "$st_nothing_needed", 0, (Sprite)null, false);
				return;
			}
			KeyValuePair<SharedData, int> keyValuePair = list.FirstOrDefault((KeyValuePair<SharedData, int> keyValuePair2) => session.CountAvailable(keyValuePair2.Key.m_name) < keyValuePair2.Value);
			if (keyValuePair.Key != null)
			{
				((Character)localPlayer).Message((MessageType)2, Localization.instance.Localize("$st_missing: " + keyValuePair.Key.m_name), 0, (Sprite)null, false);
				return;
			}
			int num3 = 0;
			float num4 = 0f;
			foreach (KeyValuePair<SharedData, int> m in list)
			{
				int maxStack = Math.Max(1, m.Key.m_maxStackSize);
				int num5 = (from val2 in inventory.GetAllItems()
					where val2.m_shared.m_name == m.Key.m_name && val2.m_stack < maxStack
					select val2).Sum((ItemData val2) => maxStack - val2.m_stack);
				int num6 = m.Value - num5;
				if (num6 > 0)
				{
					num3 += (num6 + maxStack - 1) / maxStack;
				}
				num4 += m.Key.m_weight * (float)m.Value;
			}
			if (num3 > inventory.GetEmptySlots())
			{
				((Character)localPlayer).Message((MessageType)2, "$st_no_room", 0, (Sprite)null, false);
				return;
			}
			if (!Plugin.IgnoreWeight.Value && inventory.GetTotalWeight() + num4 > localPlayer.GetMaxCarryWeight())
			{
				((Character)localPlayer).Message((MessageType)2, "$st_too_heavy", 0, (Sprite)null, false);
				return;
			}
			foreach (KeyValuePair<SharedData, int> item in list)
			{
				session.TakeResource(item.Key.m_name, item.Value, inventory);
			}
			((Character)localPlayer).Message((MessageType)2, "$st_taken", 0, (Sprite)null, false);
			UpdateAmounts();
		}
	}
	internal static class ScrollStep
	{
		private static readonly Dictionary<ScrollRect, float> rows = new Dictionary<ScrollRect, float>();

		public static void Register(ScrollRect rect, float rowHeight)
		{
			if (Object.op_Implicit((Object)(object)rect))
			{
				rows[rect] = rowHeight;
			}
		}

		public static void Unregister(ScrollRect rect)
		{
			if ((Object)(object)rect != (Object)null)
			{
				rows.Remove(rect);
			}
		}

		public static bool IsRegistered(ScrollRect rect)
		{
			if ((Object)(object)rect != (Object)null)
			{
				return rows.ContainsKey(rect);
			}
			return false;
		}

		public static bool TryStep(ScrollRect rect, PointerEventData data)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: 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)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rect == (Object)null || !rows.TryGetValue(rect, out var value))
			{
				return false;
			}
			RectTransform content = rect.content;
			RectTransform val = (RectTransform)(Object.op_Implicit((Object)(object)rect.viewport) ? ((object)rect.viewport) : ((object)(RectTransform)((Component)rect).transform));
			if (!Object.op_Implicit((Object)(object)content) || value <= 0f)
			{
				return true;
			}
			Rect rect2 = content.rect;
			float height = ((Rect)(ref rect2)).height;
			rect2 = val.rect;
			float num = height - ((Rect)(ref rect2)).height;
			if (num <= 0f)
			{
				return true;
			}
			float y = data.scrollDelta.y;
			if (Mathf.Approximately(y, 0f))
			{
				return true;
			}
			int num2 = Math.Max(1, Mathf.RoundToInt(Mathf.Abs(y) / DeltaPerTick()));
			float num3 = (float)(Math.Sign(y) * num2) * value / num;
			rect.verticalNormalizedPosition = Mathf.Clamp01(rect.verticalNormalizedPosition + num3);
			return true;
		}

		private static float DeltaPerTick()
		{
			InputSystemUIInputModule val = (InputSystemUIInputModule)(Object.op_Implicit((Object)(object)EventSystem.current) ? /*isinst with value type is only supported in some contexts*/: null);
			float num = (Object.op_Implicit((Object)(object)val) ? val.scrollDeltaPerTick : 1f);
			if (!(num > 0f))
			{
				return 1f;
			}
			return num;
		}
	}
	[HarmonyPatch(typeof(ScrollRect), "OnScroll")]
	internal static class ScrollRectOnScrollPatch
	{
		private static bool Prefix(ScrollRect __instance, PointerEventData data)
		{
			try
			{
				return !ScrollStep.TryStep(__instance, data);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Scroll step failed: {arg}");
				return true;
			}
		}
	}
	internal sealed class TerminalMarker : MonoBehaviour
	{
	}
	internal static class TerminalPiece
	{
		public const string PrefabName = "piece_storage_terminal";

		private const string BasePrefab = "piece_chest";

		private static string _recipe;

		public static void Register(string recipe)
		{
			_recipe = recipe;
			PrefabManager.OnVanillaPrefabsAvailable += AddPiece;
		}

		public static bool IsTerminal(Container container)
		{
			if (Object.op_Implicit((Object)(object)container))
			{
				return Object.op_Implicit((Object)(object)((Component)container).GetComponent<TerminalMarker>());
			}
			return false;
		}

		private static void AddPiece()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			PrefabManager.OnVanillaPrefabsAvailable -= AddPiece;
			PieceConfig val = new PieceConfig
			{
				Name = "$piece_storage_terminal",
				Description = "$piece_storage_terminal_desc",
				PieceTable = PieceTables.Hammer,
				Category = PieceCategories.Furniture,
				Requirements = ParseRecipe(_recipe)
			};
			CustomPiece val2 = new CustomPiece("piece_storage_terminal", "piece_chest", val);
			if ((Object)(object)val2.PiecePrefab == (Object)null)
			{
				Plugin.Log.LogError((object)"Could not clone 'piece_chest' — the terminal will not be available.");
				return;
			}
			Container component = val2.PiecePrefab.GetComponent<Container>();
			if (Object.op_Implicit((Object)(object)component))
			{
				component.m_name = "$piece_storage_terminal";
			}
			val2.PiecePrefab.AddComponent<TerminalMarker>();
			PieceManager.Instance.AddPiece(val2);
		}

		private static RequirementConfig[] ParseRecipe(string recipe)
		{
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Expected O, but got Unknown
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Expected O, but got Unknown
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Expected O, but got Unknown
			List<RequirementConfig> list = new List<RequirementConfig>();
			string[] array = (recipe ?? "").Split(',');
			foreach (string text in array)
			{
				string[] array2 = text.Split(':');
				if (array2.Length != 2 || !int.TryParse(array2[1].Trim(), out var result) || result <= 0)
				{
					if (text.Trim().Length > 0)
					{
						Plugin.Log.LogWarning((object)("Invalid recipe entry: '" + text + "'"));
					}
				}
				else
				{
					list.Add(new RequirementConfig(array2[0].Trim(), result, 0, true));
				}
			}
			if (list.Count == 0)
			{
				list.Add(new RequirementConfig("FineWood", 10, 0, true));
				list.Add(new RequirementConfig("Iron", 2, 0, true));
			}
			return list.ToArray();
		}
	}
	internal enum SortMode
	{
		Name,
		Type,
		Amount
	}
	internal sealed class TerminalSession
	{
		private sealed class Entry
		{
			public ItemData View;

			public int Total;

			public readonly List<KeyValuePair<Container, ItemData>> Sources = new List<KeyValuePair<Container, ItemData>>();
		}

		private const int Columns = 8;

		private readonly Container _terminal;

		private readonly Inventory _original;

		private readonly Inventory _virtual;

		private readonly Dictionary<ItemData, Entry> _entries = new Dictionary<ItemData, Entry>();

		private bool _rebuilding;

		private bool _dirty;

		private bool _needsRebuild;

		private string _search = "";

		private List<ItemFilter> _filters = new List<ItemFilter>();

		public static TerminalSession Current { get; private set; }

		public Container Terminal => _terminal;

		public static bool IsView(Inventory inv)
		{
			if (inv != null && Current != null)
			{
				return inv == Current._virtual;
			}
			return false;
		}

		private TerminalSession(Container terminal)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			_terminal = terminal;
			_original = Access.ContainerInventory.Invoke(terminal);
			Inventory original = _original;
			_virtual = new Inventory("$piece_storage_terminal", (original != null) ? original.GetBkg() : null, 8, 1);
			Inventory obj = _virtual;
			obj.m_onChanged = (Action)Delegate.Combine(obj.m_onChanged, (Action)delegate
			{
				if (!_rebuilding)
				{
					_dirty = true;
				}
			});
		}

		public static void Open(Container terminal)
		{
			Current?.Close();
			TerminalSession terminalSession = new TerminalSession(terminal);
			terminalSession._filters = FilterSettings.Load();
			Access.ContainerInventory.Invoke(terminal) = terminalSession._virtual;
			Current = terminalSession;
			terminalSession.Rebuild();
		}

		public void Close()
		{
			if (Current == this)
			{
				if (_dirty && Object.op_Implicit((Object)(object)_terminal))
				{
					ApplyDiff();
				}
				if (Object.op_Implicit((Object)(object)_terminal))
				{
					Access.ContainerInventory.Invoke(_terminal) = _original;
				}
				Current = null;
			}
		}

		public void SetSearch(string search)
		{
			_search = search ?? "";
			Refresh();
		}

		public void SetFilters(List<ItemFilter> filters)
		{
			_filters = filters ?? new List<ItemFilter>();
			Refresh();
		}

		public void SetSort(SortMode mode)
		{
			Plugin.Sort.Value = mode;
			Refresh();
		}

		public void Tick(InventoryGui gui)
		{
			if (!Object.op_Implicit((Object)(object)_terminal))
			{
				Current = null;
			}
			else if ((_dirty || _needsRebuild) && Access.DragItem.Invoke(gui) == null)
			{
				Refresh();
			}
		}

		private void Refresh()
		{
			if (_dirty)
			{
				ApplyDiff();
			}
			Rebuild();
		}

		public int TakeTo(ItemData view, int amount, Inventory target, Vector2i? pos)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			if (view == null || !_entries.TryGetValue(view, out var value))
			{
				return 0;
			}
			int num = Math.Max(1, view.m_shared.m_maxStackSize);
			amount = Math.Min(Math.Min(amount, value.Total), num);
			if (amount <= 0)
			{
				return 0;
			}
			ItemData val = ((num == 1) ? value.Sources[0].Value : view).Clone();
			val.m_stack = amount;
			val.m_equipped = false;
			if (pos.HasValue)
			{
				Access.AddItemAt(target, val, val.m_stack, pos.Value);
			}
			if (val.m_stack > 0 && !FillStacks(target, val))
			{
				FillEmptySlots(target, val);
			}
			int num2 = amount - val.m_stack;
			if (num2 > 0)
			{
				RemoveFromSources(value, num2);
			}
			_needsRebuild = true;
			return num2;
		}

		public int StoreFrom(Inventory from, ItemData item, int amount)
		{
			if (from == null || item == null)
			{
				return 0;
			}
			amount = Math.Min(amount, item.m_stack);
			if (amount <= 0)
			{
				return 0;
			}
			int num = Store(item, amount);
			if (num > 0)
			{
				from.RemoveItem(item, num);
			}
			if (num < amount)
			{
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null)
				{
					((Character)localPlayer).Message((MessageType)2, "$st_no_space", 0, (Sprite)null, false);
				}
			}
			_needsRebuild = true;
			return num;
		}

		public int StackAllFrom(Inventory from)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			HashSet<string> hashSet = new HashSet<string>();
			foreach (Container item in LinkedChests.Find(((Component)_terminal).transform.position, LinkFlags.CanInsert))
			{
				foreach (ItemData allItem in Access.ContainerInventory.Invoke(item).GetAllItems())
				{
					hashSet.Add(allItem.m_shared.m_name);
				}
			}
			int num = 0;
			foreach (ItemData item2 in from.GetAllItems().ToList())
			{
				if (item2.m_shared.m_maxStackSize > 1 && !item2.m_equipped && hashSet.Contains(item2.m_shared.m_name))
				{
					num += StoreFrom(from, item2, item2.m_stack);
				}
			}
			return num;
		}

		public int CountAvailable(string name)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			foreach (Container item in LinkedChests.Find(((Component)_terminal).transform.position, LinkFlags.CanTake))
			{
				foreach (ItemData allItem in Access.ContainerInventory.Invoke(item).GetAllItems())
				{
					if (allItem.m_shared.m_name == name)
					{
						num += allItem.m_stack;
					}
				}
			}
			return num;
		}

		public int TakeResource(string name, int amount, Inventory target)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			List<KeyValuePair<Inventory, ItemData>> list = new List<KeyValuePair<Inventory, ItemData>>();
			foreach (Container item in LinkedChests.Find(((Component)_terminal).transform.position, LinkFlags.CanTake))
			{
				Inventory val = Access.ContainerInventory.Invoke(item);
				foreach (ItemData allItem in val.GetAllItems())
				{
					if (allItem.m_shared.m_name == name)
					{
						list.Add(new KeyValuePair<Inventory, ItemData>(val, allItem));
					}
				}
			}
			list.Sort((KeyValuePair<Inventory, ItemData> a, KeyValuePair<Inventory, ItemData> b) => a.Value.m_stack.CompareTo(b.Value.m_stack));
			int num = amount;
			foreach (KeyValuePair<Inventory, ItemData> item2 in list)
			{
				if (num <= 0)
				{
					break;
				}
				ItemData val2 = item2.Value.Clone();
				val2.m_equipped = false;
				val2.m_stack = Math.Min(num, item2.Value.m_stack);
				int stack = val2.m_stack;
				if (!FillStacks(target, val2))
				{
					FillEmptySlots(target, val2);
				}
				int num2 = stack - val2.m_stack;
				if (num2 > 0)
				{
					item2.Key.RemoveItem(item2.Value, num2);
				}
				num -= num2;
				if (val2.m_stack > 0)
				{
					break;
				}
			}
			_needsRebuild = true;
			return amount - num;
		}

		private void Rebuild()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			_rebuilding = true;
			try
			{
				List<Container> list = LinkedChests.Find(((Component)_terminal).transform.position, LinkFlags.CanTake);
				int num = 0;
				Dictionary<string, Entry> dictionary = new Dictionary<string, Entry>();
				List<Entry> list2 = new List<Entry>();
				foreach (Container item in list)
				{
					Inventory val = Access.ContainerInventory.Invoke(item);
					num += val.GetEmptySlots();
					foreach (ItemData allItem in val.GetAllItems())
					{
						Entry value;
						if (allItem.m_shared.m_maxStackSize > 1)
						{
							string key = allItem.m_shared.m_name + "|" + allItem.m_quality + "|" + allItem.m_worldLevel + "|" + allItem.m_variant;
							if (!dictionary.TryGetValue(key, out value))
							{
								value = (dictionary[key] = new Entry());
								list2.Add(value);
							}
						}
						else
						{
							value = new Entry();
							list2.Add(value);
						}
						value.Sources.Add(new KeyValuePair<Container, ItemData>(item, allItem));
						value.Total += allItem.m_stack;
					}
				}
				if (_filters.Count > 0)
				{
					list2 = list2.Where(delegate(Entry e)
					{
						int category = ItemCategory.Get(e.Sources[0].Value);
						return _filters.Any((ItemFilter f) => f.Matches(category));
					}).ToList();
				}
				if (_search.Length > 0)
				{
					string needle = Normalize(_search);
					list2 = list2.Where((Entry e) => Normalize(DisplayName(e.Sources[0].Value)).Contains(needle)).ToList();
				}
				Sort(list2, Plugin.Sort.Value);
				int num2 = list2.Count + num;
				_virtual.SetHeight(Math.Max(1, (num2 + 8 - 1) / 8));
				List<ItemData> list3 = Access.Items.Invoke(_virtual);
				list3.Clear();
				_entries.Clear();
				for (int num3 = 0; num3 < list2.Count; num3++)
				{
					Entry entry2 = list2[num3];
					ItemData val2 = entry2.Sources[0].Value.Clone();
					val2.m_stack = entry2.Total;
					val2.m_equipped = false;
					val2.m_gridPos = new Vector2i(num3 % 8, num3 / 8);
					entry2.View = val2;
					list3.Add(val2);
					_entries[val2] = entry2;
				}
				Access.Changed(_virtual);
			}
			finally
			{
				_rebuilding = false;
				_dirty = false;
				_needsRebuild = false;
			}
		}

		private static void Sort(List<Entry> entries, SortMode mode)
		{
			Comparison<Entry> byName = delegate(Entry a, Entry b)
			{
				ItemData value = a.Sources[0].Value;
				ItemData value2 = b.Sources[0].Value;
				int num = string.Compare(DisplayName(value), DisplayName(value2), StringComparison.CurrentCultureIgnoreCase);
				if (num != 0)
				{
					return num;
				}
				num = value2.m_quality.CompareTo(value.m_quality);
				return (num == 0) ? b.Total.CompareTo(a.Total) : num;
			};
			switch (mode)
			{
			case SortMode.Type:
			{
				Dictionary<Entry, int> groups = entries.ToDictionary((Entry e) => e, (Entry e) => ItemCategory.Get(e.Sources[0].Value));
				entries.Sort(delegate(Entry a, Entry b)
				{
					int num = groups[a].CompareTo(groups[b]);
					return (num == 0) ? byName(a, b) : num;
				});
				break;
			}
			case SortMode.Amount:
				entries.Sort(delegate(Entry a, Entry b)
				{
					int num = b.Total.CompareTo(a.Total);
					return (num == 0) ? byName(a, b) : num;
				});
				break;
			default:
				entries.Sort(byName);
				break;
			}
		}

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

		internal static string Normalize(string s)
		{
			StringBuilder stringBuilder = new StringBuilder(s.Length);
			string text = s.Normalize(NormalizationForm.FormD);
			foreach (char c in text)
			{
				if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
				{
					stringBuilder.Append(char.ToLowerInvariant(c));
				}
			}
			return stringBuilder.ToString();
		}

		private void ApplyDiff()
		{
			List<ItemData> list = Access.Items.Invoke(_virtual);
			HashSet<ItemData> hashSet = new HashSet<ItemData>(list);
			List<KeyValuePair<ItemData, int>> list2 = new List<KeyValuePair<ItemData, int>>();
			foreach (Entry value in _entries.Values)
			{
				int num = (hashSet.Contains(value.View) ? value.View.m_stack : 0);
				int num2 = value.Total - num;
				if (num2 > 0)
				{
					RemoveFromSources(value, num2);
				}
				else if (num2 < 0)
				{
					list2.Add(new KeyValuePair<ItemData, int>(value.View, -num2));
				}
			}
			foreach (ItemData item in list)
			{
				if (!_entries.ContainsKey(item))
				{
					list2.Add(new KeyValuePair<ItemData, int>(item, item.m_stack));
				}
			}
			foreach (KeyValuePair<ItemData, int> item2 in list2)
			{
				int num3 = Store(item2.Key, item2.Value);
				if (num3 < item2.Value)
				{
					ReturnToPlayer(item2.Key, item2.Value - num3);
				}
			}
			_dirty = false;
			_needsRebuild = true;
		}

		private static void RemoveFromSources(Entry entry, int amount)
		{
			foreach (KeyValuePair<Container, ItemData> item in entry.Sources.OrderBy((KeyValuePair<Container, ItemData> s) => s.Value.m_stack))
			{
				if (amount <= 0)
				{
					return;
				}
				Inventory val = (Object.op_Implicit((Object)(object)item.Key) ? Access.ContainerInventory.Invoke(item.Key) : null);
				if (val != null && val.ContainsItem(item.Value))
				{
					int num = Math.Min(amount, item.Value.m_stack);
					val.RemoveItem(item.Value, num);
					amount -= num;
				}
			}
			if (amount > 0)
			{
				Plugin.Log.LogWarning((object)$"Could not remove {amount}x {entry.View?.m_shared.m_name} — the source changed.");
			}
		}

		private int Store(ItemData template, int amount)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			ItemData carrier = template.Clone();
			carrier.m_stack = amount;
			carrier.m_equipped = false;
			List<Inventory> source = (from c in LinkedChests.Find(((Component)_terminal).transform.position, LinkFlags.CanInsert)
				select Access.ContainerInventory.Invoke(c)).ToList();
			if (source.Any((Inventory inv) => FillStacks(inv, carrier)) || source.Where((Inventory inv) => Contains(inv, carrier)).Any((Inventory inv) => FillEmptySlots(inv, carrier)))
			{
				_ = 1;
			}
			else
				source.Any((Inventory inv) => FillEmptySlots(inv, carrier));
			return amount - carrier.m_stack;
		}

		private static bool Matches(ItemData a, ItemData b)
		{
			if (a.m_shared.m_name == b.m_shared.m_name && a.m_quality == b.m_quality)
			{
				return a.m_worldLevel == b.m_worldLevel;
			}
			return false;
		}

		private static bool Contains(Inventory inv, ItemData carrier)
		{
			return inv.GetAllItems().Any((ItemData i) => Matches(i, carrier));
		}

		private static bool FillStacks(Inventory inv, ItemData carrier)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if (carrier.m_shared.m_maxStackSize <= 1)
			{
				return false;
			}
			foreach (ItemData item in inv.GetAllItems().ToList())
			{
				if (Matches(item, carrier) && item.m_stack < item.m_shared.m_maxStackSize)
				{
					Access.AddItemAt(inv, carrier, carrier.m_stack, item.m_gridPos);
					if (carrier.m_stack <= 0)
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool FillEmptySlots(Inventory inv, ItemData carrier)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			while (carrier.m_stack > 0)
			{
				Vector2i val = Access.FindEmptySlot(inv);
				if (val.x < 0)
				{
					return false;
				}
				int amount = Math.Min(carrier.m_stack, Math.Max(1, carrier.m_shared.m_maxStackSize));
				if (!Access.AddItemAt(inv, carrier, amount, val))
				{
					return false;
				}
			}
			return true;
		}

		private static void ReturnToPlayer(ItemData template, int amount)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//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_0091: 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)
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer))
			{
				return;
			}
			((Character)localPlayer).Message((MessageType)2, "$st_no_space", 0, (Sprite)null, false);
			ItemData val = template.Clone();
			val.m_stack = amount;
			val.m_equipped = false;
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			if (!FillStacks(inventory, val) && !FillEmptySlots(inventory, val))
			{
				Transform transform = ((Component)localPlayer).transform;
				int val2 = Math.Max(1, val.m_shared.m_maxStackSize);
				while (val.m_stack > 0)
				{
					int num = Math.Min(val.m_stack, val2);
					ItemDrop.DropItem(val, num, transform.position + transform.forward + Vector3.up, transform.rotation);
					val.m_stack -= num;
				}
			}
		}
	}
	internal static class TerminalUi
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static Action <0>__OnTerminalOpened;

			public static UnityAction <1>__Toggle;
		}

		private const float BarY = -24f;

		private const float FilterTop = -46f;

		private static InventoryGui _createdFor;

		private static GameObject _terminalRoot;

		private static GameObject _chestRoot;

		private static InputField _search;

		private static Text _filterButtonText;

		private static Toggle _take;

		private static Toggle _insert;

		private static Container _shownChest;

		private static ScrollRect _gridScroll;

		private static bool _blockingInput;

		public static void OnTerminalOpened()
		{
			if (Object.op_Implicit((Object)(object)_search))
			{
				_search.SetTextWithoutNotify("");
			}
			Safe("filter sync", delegate
			{
				FilterPanel.Sync(FilterSettings.Load());
			});
			Safe("recipe loading", RecipePanel.OnTerminalOpened);
		}

		public static void Update(InventoryGui gui)
		{
			if (!((Object)(object)_createdFor != (Object)(object)gui) || Create(gui))
			{
				Container val = Access.CurrentContainer.Invoke(gui);
				bool flag = Object.op_Implicit((Object)(object)val) && TerminalPiece.IsTerminal(val);
				bool flag2 = Object.op_Implicit((Object)(object)val) && !flag && LinkedChests.IsLinkable(val);
				SetActive(_terminalRoot, flag);
				RecipePanel.SetActive(flag);
				SetActive(_chestRoot, flag2);
				if (flag && Object.op_Implicit((Object)(object)gui.m_takeAllButton))
				{
					SetActive(((Component)gui.m_takeAllButton).gameObject, active: false);
				}
				if (Object.op_Implicit((Object)(object)_filterButtonText))
				{
					_filterButtonText.text = (FilterPanel.IsOpen ? "Filters ▲" : "Filters ▼");
				}
				UpdateScrollTarget(gui, flag);
				if (!flag2 || !Object.op_Implicit((Object)(object)_take) || !Object.op_Implicit((Object)(object)_insert))
				{
					_shownChest = null;
				}
				else if ((Object)(object)val != (Object)(object)_shownChest)
				{
					_shownChest = val;
					_take.SetIsOnWithoutNotify(LinkFlags.CanTake(val));
					_insert.SetIsOnWithoutNotify(LinkFlags.CanInsert(val));
				}
				bool flag3 = flag && ((Object.op_Implicit((Object)(object)_search) && _search.isFocused) || RecipePanel.IsFocused);
				if (flag3 != _blockingInput)
				{
					SetInputBlock(flag3);
				}
			}
		}

		public static void ReleaseInput()
		{
			if (_blockingInput)
			{
				SetInputBlock(block: false);
			}
		}

		private static void SetInputBlock(bool block)
		{
			_blockingInput = block;
			GUIManager.BlockInput(block);
		}

		private static void UpdateScrollTarget(InventoryGui gui, bool terminal)
		{
			if (!terminal)
			{
				ScrollStep.Unregister(_gridScroll);
			}
			else if (!ScrollStep.IsRegistered(_gridScroll))
			{
				InventoryGrid val = Access.ContainerGrid.Invoke(gui);
				_gridScroll = ((Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.m_gridRoot)) ? ((Component)val.m_gridRoot).GetComponentInParent<ScrollRect>() : null);
				if (!Object.op_Implicit((Object)(object)_gridScroll))
				{
					Plugin.Log.LogWarning((object)"Terminal grid has no ScrollRect — row scrolling disabled.");
				}
				else
				{
					ScrollStep.Register(_gridScroll, val.m_elementSpace);
				}
			}
		}

		private static bool Create(InventoryGui gui)
		{
			if (!Object.op_Implicit((Object)(object)gui.m_container) || GUIManager.Instance == null || !Object.op_Implicit((Object)(object)GUIManager.Instance.AveriaSerifBold) || Localization.instance == null)
			{
				return false;
			}
			_createdFor = gui;
			_shownChest = null;
			_blockingInput = false;
			ScrollStep.Unregister(_gridScroll);
			_gridScroll = null;
			RecipePanel.Destroy();
			RectTransform panel = gui.m_container;
			Safe("terminal bar", delegate
			{
				CreateTerminalBar(panel);
			});
			Safe("chest checkboxes", delegate
			{
				CreateChestFlags((Transform)(object)panel);
			});
			Safe("recipe panel", delegate
			{
				RecipePanel.Create((Transform)(object)panel);
			});
			if (TerminalSession.Current != null)
			{
				OnTerminalOpened();
			}
			return true;
		}

		private static void CreateTerminalBar(RectTransform panel)
		{
			//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_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Expected O, but got Unknown
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			_terminalRoot = NewRoot("StorageTerminal_Bar", (Transform)(object)panel);
			_terminalRoot.SetActive(false);
			GUIManager instance = GUIManager.Instance;
			_search = instance.CreateInputField(_terminalRoot.transform, Vector2.zero, Vector2.zero, new Vector2(105f, -24f), (ContentType)0, Localization.instance.Localize("$st_search"), 16, 190f, 32f).GetComponent<InputField>();
			((UnityEvent<string>)(object)_search.onValueChanged).AddListener((UnityAction<string>)delegate(string text)
			{
				TerminalSession.Current?.SetSearch(text);
			});
			AddSortButton("$st_sort_name", SortMode.Name, 250f);
			AddSortButton("$st_sort_type", SortMode.Type, 335f);
			AddSortButton("$st_sort_amount", SortMode.Amount, 420f);
			GameObject obj = instance.CreateButton("Filters ▼", _terminalRoot.transform, Vector2.zero, Vector2.zero, new Vector2(515f, -24f), 100f, 32f);
			ButtonClickedEvent onClick = obj.GetComponent<Button>().onClick;
			object obj2 = <>O.<1>__Toggle;
			if (obj2 == null)
			{
				UnityAction val = FilterPanel.Toggle;
				<>O.<1>__Toggle = val;
				obj2 = (object)val;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj2);
			_filterButtonText = obj.GetComponentInChildren<Text>();
			Rect rect = panel.rect;
			float num;
			if (!(((Rect)(ref rect)).width > 100f))
			{
				num = 570f;
			}
			else
			{
				rect = panel.rect;
				num = ((Rect)(ref rect)).width;
			}
			float width = num;
			FilterPanel.Create(_terminalRoot.transform, width, -46f);
		}

		private static void CreateChestFlags(Transform panel)
		{
			_chestRoot = NewRoot("StorageTerminal_Flags", panel);
			_chestRoot.SetActive(false);
			_insert = AddToggle("$st_insert", 20f, delegate(bool v)
			{
				if (Object.op_Implicit((Object)(object)_shownChest))
				{
					LinkFlags.SetInsert(_shownChest, v);
				}
			});
			_take = AddToggle("$st_take", 280f, delegate(bool v)
			{
				if (Object.op_Implicit((Object)(object)_shownChest))
				{
					LinkFlags.SetTake(_shownChest, v);
				}
			});
		}

		private static GameObject NewRoot(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_001f: 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_002c: 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_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent(parent, false);
			val2.anchorMin = Vector2.zero;
			val2.anchorMax = Vector2.one;
			Vector2 offsetMin = (val2.offsetMax = Vector2.zero);
			val2.offsetMin = offsetMin;
			return val;
		}

		private static void AddSortButton(string token, SortMode mode, float x)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			((UnityEvent)GUIManager.Instance.CreateButton(Localization.instance.Localize(token), _terminalRoot.transform, Vector2.zero, Vector2.zero, new Vector2(x, -24f), 80f, 32f).GetComponent<Button>().onClick).AddListener((UnityAction)delegate
			{
				TerminalSession.Current?.SetSort(mode);
			});
		}

		private static Toggle AddToggle(string token, float x, Action<bool> onChanged)
		{
			//IL_0031: 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_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = GUIManager.Instance.CreateToggle(_chestRoot.transform, 28f, 28f);
			RectTransform val = (RectTransform)obj.transform;
			Vector2 anchorMin = (val.anchorMax = Vector2.zero);
			val.anchorMin = anchorMin;
			val.anchoredPosition = new Vector2(x + 14f, -24f);
			GUIManager.Instance.CreateText(Localization.instance.Localize(token), _chestRoot.transform, Vector2.zero, Vector2.zero, new Vector2(x + 140f, -24f), GUIManager.Instance.AveriaSerifBold, 16, GUIManager.Instance.ValheimOrange, true, Color.black, 220f, 30f, false);
			Toggle component = obj.GetComponent<Toggle>();
			((UnityEvent<bool>)(object)component.onValueChanged).AddListener((UnityAction<bool>)delegate(bool v)
			{
				onChanged(v);
			});
			return component;
		}

		private static void SetActive(GameObject go, bool active)
		{
			if (Object.op_Implicit((Object)(object)go) && go.activeSelf != active)
			{
				go.SetActive(active);
			}
		}

		private static void Safe(string what, Action action)
		{
			try
			{
				action();
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"UI ({what}) failed: {arg}");
			}
		}
	}
	internal static class Texts
	{
		public static void Register()
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			string text = "English";
			localization.AddTranslation(ref text, new Dictionary<string, string>
			{
				{ "piece_storage_terminal", "Storage terminal" },
				{ "piece_storage_terminal_desc", "Shows the contents of all linked chests in range and stores items into them." },
				{ "st_take", "Enable terminal withdraw" },
				{ "st_insert", "Enable terminal insertion" },
				{ "st_search", "Search..." },
				{ "st_sort_name", "A-Z" },
				{ "st_sort_type", "Type" },
				{ "st_sort_amount", "Amount" },
				{ "st_no_space", "No space in linked chests" },
				{ "st_enabled_all", "Terminal enabled on $1 chests" },
				{ "st_recipes", "Recipes" },
				{ "st_recipe_search", "Search recipe..." },
				{ "st_amount", "Amount" },
				{ "st_take_button", "Take" },
				{ "st_select_recipe", "Select a recipe" },
				{ "st_no_room", "Not enough room in inventory" },
				{ "st_too_heavy", "You can't carry that much" },
				{ "st_missing", "Not enough materials" },
				{ "st_taken", "Materials taken" },
				{ "st_nothing_needed", "You already have everything" },
				{ "st_ignore_weight", "Ignore carry weight" }
			});
		}
	}
}