Decompiled source of ChestTerminal v0.4.1

plugins/ChestTerminal.dll

Decompiled 6 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
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;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ChestTerminal
{
	public enum Category
	{
		All,
		Materials,
		Food,
		Potions,
		Weapons,
		Armor,
		Ammo,
		Tools,
		Trophies,
		Fish,
		Misc
	}
	internal static class Catalog
	{
		private static bool _changedResolved;

		private static MethodInfo _changed;

		private static MethodInfo _updateWeight;

		private static FieldInfo _onChanged;

		private static MethodInfo _showMessage;

		public static List<Container> FindContainers(Vector3 center, float radius)
		{
			//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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			List<Container> list = new List<Container>();
			PlayerProfile val = (Object.op_Implicit((Object)(object)Game.instance) ? Game.instance.GetPlayerProfile() : null);
			if (val == null)
			{
				return list;
			}
			long playerID = val.GetPlayerID();
			float num = radius * radius;
			Container[] array = Object.FindObjectsOfType<Container>();
			foreach (Container val2 in array)
			{
				if (Object.op_Implicit((Object)(object)val2) && !Terminal.Is(val2))
				{
					Vector3 val3 = ((Component)val2).transform.position - center;
					if (!(((Vector3)(ref val3)).sqrMagnitude > num) && !((Object)(object)val2.m_nview == (Object)null) && val2.m_nview.IsValid() && val2.GetInventory() != null && !Object.op_Implicit((Object)(object)((Component)val2).GetComponent<TombStone>()) && (Plugin.IncludeCartsAndShips.Value || (!Object.op_Implicit((Object)(object)val2.m_wagon) && !Object.op_Implicit((Object)(object)((Component)val2).GetComponentInParent<Vagon>()) && !Object.op_Implicit((Object)(object)((Component)val2).GetComponentInParent<Ship>()))) && val2.CheckAccess(playerID) && (!val2.m_checkGuardStone || PrivateArea.CheckAccess(((Component)val2).transform.position, 0f, false, false)))
					{
						list.Add(val2);
					}
				}
			}
			return list;
		}

		public static bool Stackable(ItemData item)
		{
			return item.m_shared.m_maxStackSize > 1;
		}

		public static string Key(ItemData item)
		{
			return item.m_shared.m_name + "|" + item.m_quality + "|" + item.m_variant + "|" + item.m_worldLevel;
		}

		public static string SearchText(ItemData item)
		{
			string text = Localization.instance.Localize(item.m_shared.m_name);
			string text2 = (Object.op_Implicit((Object)(object)item.m_dropPrefab) ? ((Object)item.m_dropPrefab).name : "");
			return (text + " " + text2 + " " + Loc.Category(CategoryOf(item))).ToLowerInvariant();
		}

		public static Category CategoryOf(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_0074: Expected I4, but got Unknown
			ItemType itemType = item.m_shared.m_itemType;
			switch (itemType - 1)
			{
			case 0:
				return Category.Materials;
			case 1:
				if (!(item.m_shared.m_food > 0f))
				{
					return Category.Potions;
				}
				return Category.Food;
			case 2:
			case 3:
			case 13:
			case 19:
			case 21:
				return Category.Weapons;
			case 4:
			case 5:
			case 6:
			case 10:
			case 11:
			case 16:
			case 17:
			case 23:
				return Category.Armor;
			case 8:
			case 22:
				return Category.Ammo;
			case 14:
			case 18:
				return Category.Tools;
			case 12:
				return Category.Trophies;
			case 20:
				return Category.Fish;
			default:
				return Category.Misc;
			}
		}

		public static int MaxFit(Inventory target, ItemData item, int amount)
		{
			if (amount <= 0)
			{
				return 0;
			}
			if (target.CanAddItem(item, amount))
			{
				return amount;
			}
			int num = 0;
			int num2 = amount;
			while (num < num2)
			{
				int num3 = (num + num2 + 1) / 2;
				if (target.CanAddItem(item, num3))
				{
					num = num3;
				}
				else
				{
					num2 = num3 - 1;
				}
			}
			return num;
		}

		public static void NotifyChanged(Inventory inv)
		{
			if (inv == null)
			{
				return;
			}
			if (!_changedResolved)
			{
				_changedResolved = true;
				_changed = typeof(Inventory).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Changed" && m.GetParameters().All((ParameterInfo p) => p.IsOptional));
				_updateWeight = typeof(Inventory).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "UpdateTotalWeight" && m.GetParameters().Length == 0);
				_onChanged = typeof(Inventory).GetField("m_onChanged", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				Plugin.Log.LogInfo((object)((_changed != null) ? $"Inventory change notification: {_changed}" : $"Inventory.Changed not found, using fallback (weight: {_updateWeight != null}, event: {_onChanged != null})"));
			}
			if (_changed != null)
			{
				_changed.Invoke(inv, (from p in _changed.GetParameters()
					select p.DefaultValue).ToArray());
			}
			else
			{
				_updateWeight?.Invoke(inv, null);
				(_onChanged?.GetValue(inv) as Action)?.Invoke();
			}
		}

		public static void ShowMessage(MessageType type, string text)
		{
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)MessageHud.instance))
			{
				return;
			}
			if (_showMessage == null)
			{
				_showMessage = (from m in typeof(MessageHud).GetMethods(BindingFlags.Instance | BindingFlags.Public)
					where m.Name == "ShowMessage"
					orderby m.GetParameters().Length descending
					select m).FirstOrDefault(delegate(MethodInfo m)
				{
					ParameterInfo[] parameters = m.GetParameters();
					return parameters.Length >= 2 && parameters[0].ParameterType == typeof(MessageType) && parameters[1].ParameterType == typeof(string) && parameters.Skip(2).All((ParameterInfo p) => p.IsOptional);
				});
			}
			if (!(_showMessage == null))
			{
				object[] array = (from p in _showMessage.GetParameters()
					select p.DefaultValue).ToArray();
				array[0] = type;
				array[1] = text;
				_showMessage.Invoke(MessageHud.instance, array);
			}
		}

		public static bool EnsureOwner(Container c)
		{
			ZNetView nview = c.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return false;
			}
			if (!nview.IsOwner())
			{
				nview.ClaimOwnership();
			}
			return nview.IsOwner();
		}
	}
	internal static class Loc
	{
		private static readonly Dictionary<string, string> En = new Dictionary<string, string>
		{
			{ "open", "Open terminal (radius {0} m)" },
			{ "search", "Search item or category..." },
			{ "status", "Chests in range: {0}   Radius: {1} m" },
			{ "chests_full", "No room in the chests, item returned" },
			{ "title", "Chest terminal" },
			{ "set_radius", "Chest radius" },
			{ "set_radius_tip", "Chests within this many meters of the terminal are shown." },
			{ "set_columns", "Window width (slots)" },
			{ "set_columns_tip", "How many slots wide the terminal window is. Applies the next time you open it." },
			{ "set_merge", "One slot per item" },
			{ "set_merge_tip", "Show all items of one kind as a single slot with the total amount. You take out up to one full stack at a time." },
			{ "set_carts", "Include carts and ships" },
			{ "set_carts_tip", "Also show what is stored in carts and ships in range." },
			{ "cat_All", "All" },
			{ "cat_Materials", "Materials" },
			{ "cat_Food", "Food" },
			{ "cat_Potions", "Meads" },
			{ "cat_Weapons", "Weapons" },
			{ "cat_Armor", "Armor" },
			{ "cat_Ammo", "Ammo" },
			{ "cat_Tools", "Tools" },
			{ "cat_Trophies", "Trophies" },
			{ "cat_Fish", "Fish" },
			{ "cat_Misc", "Misc" }
		};

		private static readonly Dictionary<string, string> Ru = new Dictionary<string, string>
		{
			{ "open", "Открыть терминал (радиус {0} м)" },
			{ "search", "Поиск предмета или категории..." },
			{ "status", "Сундуков рядом: {0}   Радиус: {1} м" },
			{ "chests_full", "В сундуках нет места, предмет возвращён" },
			{ "title", "Терминал сундуков" },
			{ "set_radius", "Радиус сундуков" },
			{ "set_radius_tip", "Показываются сундуки в пределах этого расстояния от терминала, в метрах." },
			{ "set_columns", "Ширина окна (ячеек)" },
			{ "set_columns_tip", "Сколько ячеек в ширину у окна терминала. Применяется при следующем открытии." },
			{ "set_merge", "Одна ячейка на предмет" },
			{ "set_merge_tip", "Показывать одинаковые предметы одной ячейкой с общим количеством. За раз берётся до одной полной стопки." },
			{ "set_carts", "Телеги и корабли" },
			{ "set_carts_tip", "Показывать также содержимое телег и кораблей в радиусе." },
			{ "cat_All", "Все" },
			{ "cat_Materials", "Материалы" },
			{ "cat_Food", "Еда" },
			{ "cat_Potions", "Зелья" },
			{ "cat_Weapons", "Оружие" },
			{ "cat_Armor", "Броня" },
			{ "cat_Ammo", "Боеприпасы" },
			{ "cat_Tools", "Инструменты" },
			{ "cat_Trophies", "Трофеи" },
			{ "cat_Fish", "Рыба" },
			{ "cat_Misc", "Прочее" }
		};

		public static bool IsRussian
		{
			get
			{
				if (Localization.instance != null)
				{
					return Localization.instance.GetSelectedLanguage() == "Russian";
				}
				return false;
			}
		}

		public static string T(string key)
		{
			if (!(IsRussian ? Ru : En).TryGetValue(key, out var value))
			{
				return key;
			}
			return value;
		}

		public static string Category(Category c)
		{
			return T("cat_" + c);
		}
	}
	internal static class Terminal
	{
		public static bool Is(Container c)
		{
			if (Object.op_Implicit((Object)(object)c))
			{
				return ((Object)((Component)c).gameObject).name.StartsWith("piece_chestterminal");
			}
			return false;
		}

		public static IEnumerable<MethodBase> Methods(string name)
		{
			return from m in typeof(Container).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
				where m.Name == name && m.DeclaringType == typeof(Container)
				select m;
		}
	}
	[HarmonyPatch]
	internal static class ContainerInteractPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Terminal.Methods("Interact");
		}

		[HarmonyPriority(800)]
		private static void Prefix(Container __instance, object[] __args)
		{
			if (!Terminal.Is(__instance))
			{
				return;
			}
			Humanoid val = (Humanoid)((__args.Length != 0) ? /*isinst with value type is only supported in some contexts*/: null);
			bool flag = default(bool);
			int num;
			if (__args.Length > 1)
			{
				object obj = __args[1];
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
			}
			else
			{
				num = 0;
			}
			if (((uint)num & (flag ? 1u : 0u)) == 0 && Object.op_Implicit((Object)(object)val) && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && Object.op_Implicit((Object)(object)TerminalSession.Instance) && !TerminalSession.IsViewing(__instance))
			{
				TerminalSession.Instance.Begin(__instance);
			}
		}
	}
	[HarmonyPatch]
	internal static class ContainerHoverPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Terminal.Methods("GetHoverText");
		}

		[HarmonyPriority(0)]
		private static void Postfix(Container __instance, ref string __result)
		{
			if (Terminal.Is(__instance))
			{
				__result = Localization.instance.Localize("$piece_chestterminal\n[<color=yellow><b>$KEY_Use</b></color>] ") + string.Format(Loc.T("open"), Mathf.RoundToInt(Plugin.Radius.Value));
			}
		}
	}
	[HarmonyPatch]
	internal static class ContainerSavePatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Terminal.Methods("Save");
		}

		[HarmonyPriority(800)]
		private static bool Prefix(Container __instance)
		{
			return !Terminal.Is(__instance);
		}
	}
	[HarmonyPatch]
	internal static class ContainerLoadPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return Terminal.Methods("Load");
		}

		[HarmonyPriority(800)]
		private static bool Prefix(Container __instance)
		{
			return !Terminal.Is(__instance);
		}
	}
	[HarmonyPatch]
	internal static class ZInputPatch
	{
		private static readonly string[] Names = new string[6] { "GetButtonDown", "GetButton", "GetButtonUp", "GetKeyDown", "GetKey", "GetKeyUp" };

		private static IEnumerable<MethodBase> TargetMethods()
		{
			return from m in typeof(ZInput).GetMethods(BindingFlags.Static | BindingFlags.Public)
				where Names.Contains(m.Name) && m.ReturnType == typeof(bool) && m.DeclaringType == typeof(ZInput)
				select m;
		}

		private static bool Prefix(ref bool __result)
		{
			if (!TerminalSession.SearchFocused)
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[BepInPlugin("asinit.ChestTerminal", "ChestTerminal", "0.4.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "asinit.ChestTerminal";

		public const string ModName = "ChestTerminal";

		public const string Version = "0.4.1";

		public const string PrefabName = "piece_chestterminal";

		internal static Plugin Instance;

		internal static ManualLogSource Log;

		internal static ConfigEntry<float> Radius;

		internal static ConfigEntry<bool> IncludeCartsAndShips;

		internal static ConfigEntry<string> Recipe;

		internal static ConfigEntry<float> UiScale;

		internal static ConfigEntry<float> SearchBarOffset;

		internal static ConfigEntry<int> Columns;

		internal static ConfigEntry<bool> MergeStacks;

		internal static ConfigEntry<string> BasePrefab;

		internal static ConfigEntry<bool> Glow;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Expected O, but got Unknown
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Expected O, but got Unknown
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Expected O, but got Unknown
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Expected O, but got Unknown
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Radius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Radius", 20f, new ConfigDescription("Radius (meters) around the terminal in which chests are scanned. / Радиус поиска сундуков вокруг терминала (м).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
			IncludeCartsAndShips = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "IncludeCartsAndShips", false, "Also show the contents of carts and ships in range. / Показывать также содержимое телег и кораблей.");
			Recipe = ((BaseUnityPlugin)this).Config.Bind<string>("Piece", "Recipe", "Wood:20,Resin:4", "Build cost as Item:Amount pairs separated by commas (restart required). / Стоимость постройки, Предмет:Кол-во через запятую (нужен перезапуск).");
			if (Recipe.Value == "FineWood:10,Bronze:2,SurtlingCore:1")
			{
				Recipe.Value = (string)((ConfigEntryBase)Recipe).DefaultValue;
			}
			BasePrefab = ((BaseUnityPlugin)this).Config.Bind<string>("Piece", "BasePrefab", "piece_chest_blackmetal", "Vanilla chest the terminal takes its components and materials from; the model itself is the terminal's own (restart required). / Сундук игры, из которого берутся компоненты и материалы; модель у терминала своя (нужен перезапуск).");
			Glow = ((BaseUnityPlugin)this).Config.Bind<bool>("Piece", "Glow", true, "Soft blue light from the terminal crystal (restart required). / Синяя подсветка от кристалла терминала (нужен перезапуск).");
			UiScale = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "Scale", 1f, new ConfigDescription("Search bar scale. / Масштаб панели поиска.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
			Columns = ((BaseUnityPlugin)this).Config.Bind<int>("UI", "Columns", 8, new ConfigDescription("Width of the terminal window in slots. / Ширина окна терминала в ячейках.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 16), Array.Empty<object>()));
			MergeStacks = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "MergeStacks", true, "Show all items of one kind as a single slot with the total amount. / Показывать одинаковые предметы одной ячейкой с общим количеством.");
			SearchBarOffset = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "SearchBarOffset", 12f, new ConfigDescription("Distance of the search bar from the top of the screen (at 1080p). / Отступ панели поиска от верха экрана.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 900f), Array.Empty<object>()));
			AddLocalizations();
			PrefabManager.OnVanillaPrefabsAvailable += AddPiece;
			PrefabManager.OnVanillaPrefabsAvailable += AddSeneaLSettings;
			GameObject val = new GameObject("ChestTerminal_Session");
			Object.DontDestroyOnLoad((Object)val);
			val.AddComponent<TerminalSession>();
			_harmony = new Harmony("asinit.ChestTerminal");
			Type[] array = new Type[11]
			{
				typeof(ContainerInteractPatch),
				typeof(ContainerHoverPatch),
				typeof(ContainerSavePatch),
				typeof(ContainerLoadPatch),
				typeof(ZInputPatch),
				typeof(MoveOutPatch),
				typeof(MoveAtPatch),
				typeof(MoveAllPatch),
				typeof(DragPatch),
				typeof(SplitPatch),
				typeof(DropPatch)
			};
			foreach (Type type in array)
			{
				try
				{
					_harmony.CreateClassProcessor(type).Patch();
				}
				catch (Exception arg)
				{
					Log.LogError((object)$"Harmony patch {type.Name} failed: {arg}");
				}
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void AddPiece()
		{
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Expected O, but got Unknown
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: 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_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Expected O, but got Unknown
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				GameObject val = PrefabManager.Instance.CreateClonedPrefab("piece_chestterminal", BasePrefab.Value);
				if (!Object.op_Implicit((Object)(object)val) && BasePrefab.Value != "piece_chest_wood")
				{
					Log.LogWarning((object)("Base prefab '" + BasePrefab.Value + "' not found, using piece_chest_wood"));
					val = PrefabManager.Instance.CreateClonedPrefab("piece_chestterminal", "piece_chest_wood");
				}
				if (!Object.op_Implicit((Object)(object)val))
				{
					Log.LogError((object)"No base chest prefab found, terminal piece not added");
					return;
				}
				Container component = val.GetComponent<Container>();
				if (Object.op_Implicit((Object)(object)component))
				{
					component.m_name = "$piece_chestterminal";
				}
				TerminalModel.Build(val);
				PieceConfig val2 = new PieceConfig
				{
					Name = "$piece_chestterminal",
					Description = "$piece_chestterminal_desc",
					PieceTable = "Hammer",
					Category = "Furniture",
					CraftingStation = "piece_workbench",
					Requirements = ParseRecipe(Recipe.Value)
				};
				try
				{
					val2.Icon = RenderManager.Instance.Render(val, RenderManager.IsometricRotation);
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("Could not render the terminal icon, using the chest icon: " + ex.Message));
				}
				PieceManager.Instance.AddPiece(new CustomPiece(val, false, val2));
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Failed to add terminal piece: {arg}");
			}
			finally
			{
				PrefabManager.OnVanillaPrefabsAvailable -= AddPiece;
			}
		}

		private static void AddSeneaLSettings()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= AddSeneaLSettings;
			SeneaL.AddSetting((ConfigEntryBase)(object)Radius, () => Loc.T("set_radius"), () => Loc.T("set_radius_tip"));
			SeneaL.AddSetting((ConfigEntryBase)(object)Columns, () => Loc.T("set_columns"), () => Loc.T("set_columns_tip"));
			SeneaL.AddSetting((ConfigEntryBase)(object)MergeStacks, () => Loc.T("set_merge"), () => Loc.T("set_merge_tip"));
			SeneaL.AddSetting((ConfigEntryBase)(object)IncludeCartsAndShips, () => Loc.T("set_carts"), () => Loc.T("set_carts_tip"));
		}

		private static RequirementConfig[] ParseRecipe(string recipe)
		{
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Expected O, but got Unknown
			List<RequirementConfig> list = new List<RequirementConfig>();
			string[] array = (recipe ?? "").Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text in array)
			{
				string[] array2 = text.Split(new char[1] { ':' });
				if (array2.Length != 2 || !int.TryParse(array2[1].Trim(), out var result) || result <= 0)
				{
					Log.LogWarning((object)("Ignoring bad recipe entry '" + text + "'"));
				}
				else
				{
					list.Add(new RequirementConfig(array2[0].Trim(), result, 0, true));
				}
			}
			if (list.Count == 0)
			{
				list.Add(new RequirementConfig("Wood", 10, 0, true));
			}
			return list.ToArray();
		}

		private static void AddLocalizations()
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			string text = "English";
			localization.AddTranslation(ref text, new Dictionary<string, string>
			{
				{ "piece_chestterminal", "Chest terminal" },
				{ "piece_chestterminal_desc", "Opens all chests nearby as one big chest, with search and categories." }
			});
			text = "Russian";
			localization.AddTranslation(ref text, new Dictionary<string, string>
			{
				{ "piece_chestterminal", "Терминал сундуков" },
				{ "piece_chestterminal_desc", "Открывает все сундуки поблизости как один большой сундук, с поиском и категориями." }
			});
		}
	}
	internal class SearchBar
	{
		public struct Result
		{
			public string Search;

			public Category Category;

			public bool Focused;
		}

		private const float Width = 900f;

		private const string SearchControl = "ChestTerminal_Search";

		private static readonly Category[] Categories = (Category[])Enum.GetValues(typeof(Category));

		private bool _stylesReady;

		private GUIStyle _panel;

		private GUIStyle _field;

		private GUIStyle _placeholder;

		private GUIStyle _tab;

		private GUIStyle _tabActive;

		private GUIStyle _info;

		public Result Draw(string search, Category category, int chestCount)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Invalid comparison between Unknown and I4
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Invalid comparison between Unknown and I4
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Invalid comparison between Unknown and I4
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: 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_0126: Invalid comparison between Unknown and I4
			//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f5: Unknown result type (might be due to invalid IL or missing references)
			EnsureStyles();
			Result result = new Result
			{
				Search = search,
				Category = category
			};
			Matrix4x4 matrix = GUI.matrix;
			float num = (float)Screen.height / 1080f * Plugin.UiScale.Value;
			GUI.matrix = Matrix4x4.Scale(new Vector3(num, num, 1f));
			float num2 = (float)Screen.width / num;
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(Mathf.Round((num2 - 900f) / 2f), Plugin.SearchBarOffset.Value, 900f, 92f);
			Event current = Event.current;
			if ((int)current.type == 0 && !((Rect)(ref val)).Contains(current.mousePosition))
			{
				GUI.FocusControl((string)null);
			}
			GUI.Box(val, GUIContent.none, _panel);
			GUI.BeginGroup(val);
			Rect val2 = default(Rect);
			((Rect)(ref val2))..ctor(12f, 10f, 420f, 32f);
			bool flag = GUI.GetNameOfFocusedControl() == "ChestTerminal_Search";
			if (flag && (int)current.type == 4 && ((int)current.keyCode == 27 || (int)current.keyCode == 13 || (int)current.keyCode == 271))
			{
				GUI.FocusControl((string)null);
				current.Use();
				flag = false;
			}
			GUI.SetNextControlName("ChestTerminal_Search");
			result.Search = GUI.TextField(val2, search ?? "", 64, _field);
			if (string.IsNullOrEmpty(result.Search) && !flag)
			{
				GUI.Label(new Rect(((Rect)(ref val2)).x + 10f, ((Rect)(ref val2)).y, ((Rect)(ref val2)).width - 10f, ((Rect)(ref val2)).height), Loc.T("search"), _placeholder);
			}
			if (!string.IsNullOrEmpty(result.Search) && GUI.Button(new Rect(((Rect)(ref val2)).xMax + 6f, ((Rect)(ref val2)).y, 32f, 32f), "X", _tab))
			{
				result.Search = "";
				GUI.FocusControl((string)null);
			}
			GUI.Label(new Rect(480f, 10f, 408f, 32f), string.Format(Loc.T("status"), chestCount, Mathf.RoundToInt(Plugin.Radius.Value)), _info);
			float num3 = 12f;
			float num4 = 4f;
			float num5 = (876f - num4 * (float)(Categories.Length - 1)) / (float)Categories.Length;
			Category[] categories = Categories;
			foreach (Category category2 in categories)
			{
				if (GUI.Button(new Rect(num3, 50f, num5, 30f), Loc.Category(category2), (category2 == category) ? _tabActive : _tab))
				{
					result.Category = category2;
					GUI.FocusControl((string)null);
				}
				num3 += num5 + num4;
			}
			GUI.EndGroup();
			GUI.matrix = matrix;
			result.Focused = GUI.GetNameOfFocusedControl() == "ChestTerminal_Search";
			return result;
		}

		private void EnsureStyles()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			//IL_0066: Expected O, but got Unknown
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Expected O, but got Unknown
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Expected O, but got Unknown
			//IL_00db: Expected O, but got Unknown
			//IL_00ef: 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_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: 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_016c: 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_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Expected O, but got Unknown
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Expected O, but got Unknown
			//IL_01e9: 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_01f5: 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_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Expected O, but got Unknown
			//IL_0244: Expected O, but got Unknown
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_028b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c2: Expected O, but got Unknown
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_030c: Unknown result type (might be due to invalid IL or missing references)
			if (!_stylesReady)
			{
				_stylesReady = true;
				Color frame = default(Color);
				((Color)(ref frame))..ctor(0.8f, 0.66f, 0.4f);
				Color val = default(Color);
				((Color)(ref val))..ctor(0.93f, 0.88f, 0.78f);
				Font font = PickFont();
				_panel = new GUIStyle(GUI.skin.box)
				{
					border = new RectOffset(2, 2, 2, 2)
				};
				SetBackground(_panel, Framed(new Color(0.09f, 0.075f, 0.06f, 0.94f), frame));
				_field = new GUIStyle(GUI.skin.textField)
				{
					font = font,
					fontSize = 18,
					alignment = (TextAnchor)3,
					padding = new RectOffset(10, 10, 4, 4),
					border = new RectOffset(2, 2, 2, 2)
				};
				Texture2D val2 = Framed(new Color(0.04f, 0.035f, 0.03f, 1f), new Color(0.45f, 0.37f, 0.24f));
				Texture2D val3 = Framed(new Color(0.06f, 0.05f, 0.04f, 1f), frame);
				SetStates(_field, val2, val2, val3, Color.white);
				_field.focused.background = val3;
				_placeholder = new GUIStyle(GUI.skin.label)
				{
					font = font,
					fontSize = 17,
					fontStyle = (FontStyle)2,
					alignment = (TextAnchor)3
				};
				_placeholder.normal.textColor = new Color(val.r, val.g, val.b, 0.45f);
				_info = new GUIStyle(GUI.skin.label)
				{
					font = font,
					fontSize = 16,
					alignment = (TextAnchor)5
				};
				_info.normal.textColor = new Color(val.r, val.g, val.b, 0.75f);
				_tab = new GUIStyle(GUI.skin.button)
				{
					font = font,
					fontSize = 15,
					alignment = (TextAnchor)4,
					border = new RectOffset(2, 2, 2, 2)
				};
				SetStates(_tab, Framed(new Color(0.16f, 0.13f, 0.1f), new Color(0.4f, 0.33f, 0.22f)), Framed(new Color(0.24f, 0.19f, 0.13f), frame), Framed(new Color(0.3f, 0.24f, 0.15f), frame), val);
				_tabActive = new GUIStyle(_tab);
				Texture2D val4 = Framed(new Color(0.78f, 0.62f, 0.33f), new Color(0.95f, 0.82f, 0.55f));
				SetStates(_tabActive, val4, val4, val4, new Color(0.12f, 0.09f, 0.05f));
			}
		}

		private static Font PickFont()
		{
			Font val = null;
			try
			{
				GUIManager instance = GUIManager.Instance;
				val = ((instance != null) ? instance.AveriaSerifBold : null);
			}
			catch
			{
			}
			if (!Object.op_Implicit((Object)(object)val))
			{
				return null;
			}
			if (Loc.IsRussian && !val.HasCharacter('П'))
			{
				return null;
			}
			return val;
		}

		private static void SetBackground(GUIStyle style, Texture2D tex)
		{
			style.normal.background = tex;
			style.hover.background = tex;
			style.active.background = tex;
			style.focused.background = tex;
		}

		private static void SetStates(GUIStyle style, Texture2D normal, Texture2D hover, Texture2D active, Color textColor)
		{
			//IL_005a: 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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			style.normal.background = normal;
			style.hover.background = hover;
			style.active.background = active;
			style.focused.background = normal;
			style.onNormal.background = normal;
			style.onHover.background = hover;
			style.onActive.background = active;
			style.normal.textColor = textColor;
			style.hover.textColor = textColor;
			style.active.textColor = textColor;
			style.focused.textColor = textColor;
		}

		private static Texture2D Framed(Color fill, Color frame)
		{
			//IL_0004: 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_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			//IL_0037: 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)
			Texture2D val = new Texture2D(6, 6, (TextureFormat)4, false)
			{
				hideFlags = (HideFlags)61,
				filterMode = (FilterMode)0
			};
			for (int i = 0; i < 6; i++)
			{
				for (int j = 0; j < 6; j++)
				{
					val.SetPixel(j, i, (j < 2 || i < 2 || j >= 4 || i >= 4) ? frame : fill);
				}
			}
			val.Apply();
			return val;
		}
	}
	internal static class SeneaL
	{
		public const string UiGuid = "seneaL.valheim.ui";

		public const string ApiGuid = "seneaL.valheim.api";

		private static bool _resolved;

		public static Type SearchBox { get; private set; }

		public static Type ChipBar { get; private set; }

		public static bool HasWidgets
		{
			get
			{
				if (!_resolved)
				{
					_resolved = true;
					SearchBox = AccessTools.TypeByName("SeneaLUI.Components.SearchBox");
					ChipBar = AccessTools.TypeByName("SeneaLUI.Components.ChipBar");
				}
				if (SearchBox != null)
				{
					return ChipBar != null;
				}
				return false;
			}
		}

		public static void AddSetting(ConfigEntryBase entry, Func<string> label, Func<string> tip)
		{
			try
			{
				MethodInfo methodInfo = AccessTools.TypeByName("SeneaL.API.ModSettings")?.GetMethod("Add", BindingFlags.Static | BindingFlags.Public);
				if (!(methodInfo == null))
				{
					methodInfo.Invoke(null, new object[5]
					{
						"ChestTerminal",
						Loc.IsRussian ? "Терминал сундуков" : "Chest terminal",
						entry,
						label,
						tip
					});
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not add setting " + entry.Definition.Key + " to SeneaL UI: " + ex.Message));
			}
		}

		public static object Get(object target, string member)
		{
			Type type = target.GetType();
			FieldInfo field = type.GetField(member, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				return field.GetValue(target);
			}
			return type.GetProperty(member, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(target, null);
		}

		public static void Set(object target, string member, object value)
		{
			FieldInfo field = target.GetType().GetField(member, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				field.SetValue(target, value);
			}
		}

		public static object Call(object target, string method, params object[] args)
		{
			return target.GetType().GetMethod(method, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(target, args);
		}
	}
	internal class PanelBar
	{
		private static readonly string[] CategoryIcons = new string[11]
		{
			null, "ore", "fork", "spark", "swords", "helmet", "quiver", "hammer", "trophy", "anchor",
			"other"
		};

		private const float Pad = 22f;

		private const float Gap = 8f;

		private const float SearchH = 32f;

		private readonly RectTransform _panel;

		private readonly RectTransform _gridHost;

		private readonly RectTransform _gridBar;

		private readonly float _gridBaseY;

		private readonly object _search;

		private readonly object _chips;

		private readonly RectTransform _searchRoot;

		private readonly RectTransform _chipsRoot;

		private readonly PropertyInfo _focused;

		private float _applied;

		private float _lastWidth = -1f;

		private string _lastLang;

		private int _lastCat = -1;

		public bool Focused
		{
			get
			{
				try
				{
					return _focused != null && (bool)_focused.GetValue(_search, null);
				}
				catch
				{
					return false;
				}
			}
		}

		public bool Alive
		{
			get
			{
				if (Object.op_Implicit((Object)(object)_panel) && Object.op_Implicit((Object)(object)_searchRoot))
				{
					return Object.op_Implicit((Object)(object)_chipsRoot);
				}
				return false;
			}
		}

		private PanelBar(RectTransform panel, RectTransform gridHost, RectTransform gridBar, object search, object chips, RectTransform searchRoot, RectTransform chipsRoot)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			_panel = panel;
			_gridHost = gridHost;
			_gridBar = gridBar;
			_gridBaseY = gridHost.anchoredPosition.y;
			_search = search;
			_chips = chips;
			_searchRoot = searchRoot;
			_chipsRoot = chipsRoot;
			_focused = search.GetType().GetProperty("Focused");
		}

		public static PanelBar TryCreate(Action<string> onSearch, Action<int> onCategory)
		{
			if (!SeneaL.HasWidgets)
			{
				return null;
			}
			InventoryGui instance = InventoryGui.instance;
			InventoryGrid val = (Object.op_Implicit((Object)(object)instance) ? instance.m_containerGrid : null);
			RectTransform val2 = (RectTransform)(Object.op_Implicit((Object)(object)val) ? /*isinst with value type is only supported in some contexts*/: null);
			RectTransform val3 = (RectTransform)(Object.op_Implicit((Object)(object)val2) ? /*isinst with value type is only supported in some contexts*/: null);
			if (!Object.op_Implicit((Object)(object)val2) || ((Object)val2).name != "grid" || !Object.op_Implicit((Object)(object)val3) || ((Object)val3).name != "container" || !((Component)val3).gameObject.activeInHierarchy)
			{
				return null;
			}
			object obj = Activator.CreateInstance(SeneaL.SearchBox, val3, "terminal_search", 300f, "search");
			object obj2 = Activator.CreateInstance(SeneaL.ChipBar, val3, "terminal_cats", CategoryIcons, onCategory);
			object obj3 = SeneaL.Get(obj, "Root");
			RectTransform val4 = (RectTransform)((obj3 is RectTransform) ? obj3 : null);
			object obj4 = SeneaL.Get(obj2, "Root");
			RectTransform val5 = (RectTransform)((obj4 is RectTransform) ? obj4 : null);
			if (!Object.op_Implicit((Object)(object)val4) || !Object.op_Implicit((Object)(object)val5))
			{
				return null;
			}
			SeneaL.Call(obj, "SetPlaceholder", Loc.T("search"));
			obj.GetType().GetEvent("Changed")?.AddEventHandler(obj, onSearch);
			Transform obj5 = ((Transform)val3).Find("gridbar");
			return new PanelBar(val3, val2, (RectTransform)(object)((obj5 is RectTransform) ? obj5 : null), obj, obj2, val4, val5);
		}

		public void Tick(Category category)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if (!Alive)
			{
				return;
			}
			float num = Mathf.Max(200f, _panel.sizeDelta.x - 44f);
			string text = ((Localization.instance != null) ? Localization.instance.GetSelectedLanguage() : "");
			if (Mathf.Abs(num - _lastWidth) > 0.5f || text != _lastLang)
			{
				_lastWidth = num;
				_lastLang = text;
				SeneaL.Call(_search, "SetWidth", num);
				SeneaL.Call(_search, "SetPlaceholder", Loc.T("search"));
				SeneaL.Set(_chips, "MaxWidth", num);
				string[] array = new string[CategoryIcons.Length];
				for (int i = 0; i < array.Length; i++)
				{
					array[i] = Loc.Category((Category)i);
				}
				SeneaL.Call(_chips, "SetLabels", new object[1] { array });
			}
			if (category != (Category)_lastCat)
			{
				_lastCat = (int)category;
				SeneaL.Call(_chips, "SetOn", _lastCat);
			}
			float num2 = 0f - _gridBaseY;
			Place(_searchRoot, 22f, num2);
			Place(_chipsRoot, 22f, num2 + 32f + 8f);
			float num3 = ((SeneaL.Get(_chips, "Height") is float num4) ? num4 : 28f);
			ApplyOffset(40f + num3 + 14f);
		}

		private static void Place(RectTransform rt, float x, float y)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: 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_0032: 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)
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0f, 1f);
			if (rt.anchorMin != val)
			{
				Vector2 val2 = (rt.pivot = val);
				Vector2 anchorMin = (rt.anchorMax = val2);
				rt.anchorMin = anchorMin;
			}
			Vector2 val5 = default(Vector2);
			((Vector2)(ref val5))..ctor(x, 0f - y);
			if (rt.anchoredPosition != val5)
			{
				rt.anchoredPosition = val5;
			}
		}

		private void ApplyOffset(float extra)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if (Mathf.Abs(_gridHost.anchoredPosition.y - _gridBaseY) < 0.5f)
			{
				_applied = 0f;
			}
			float num = extra - _applied;
			if (!(Mathf.Abs(num) < 0.5f))
			{
				Shift(num);
				_applied = extra;
			}
		}

		private void Shift(float delta)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			RectTransform gridHost = _gridHost;
			gridHost.anchoredPosition -= new Vector2(0f, delta);
			if (Object.op_Implicit((Object)(object)_gridBar))
			{
				RectTransform gridBar = _gridBar;
				gridBar.anchoredPosition -= new Vector2(0f, delta);
			}
			RectTransform panel = _panel;
			panel.sizeDelta += new Vector2(0f, delta);
		}

		public void Destroy()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (Object.op_Implicit((Object)(object)_panel) && Object.op_Implicit((Object)(object)_gridHost) && Mathf.Abs(_gridHost.anchoredPosition.y - _gridBaseY) > 0.5f)
				{
					Shift(0f - _applied);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not restore the chest panel layout: " + ex.Message));
			}
			if (Object.op_Implicit((Object)(object)_searchRoot))
			{
				Object.Destroy((Object)(object)((Component)_searchRoot).gameObject);
			}
			if (Object.op_Implicit((Object)(object)_chipsRoot))
			{
				Object.Destroy((Object)(object)((Component)_chipsRoot).gameObject);
			}
		}
	}
	internal static class Stacks
	{
		public static bool Active => Plugin.MergeStacks.Value;

		public static bool Oversized(ItemData item)
		{
			if (item != null && Catalog.Stackable(item))
			{
				return item.m_stack > item.m_shared.m_maxStackSize;
			}
			return false;
		}

		public static int TakeStack(Inventory to, Inventory view, ItemData item)
		{
			ItemData val = item.Clone();
			val.m_stack = Mathf.Min(item.m_stack, item.m_shared.m_maxStackSize);
			val.m_equipped = false;
			int stack = val.m_stack;
			int num = (to.AddItem(val) ? stack : (stack - val.m_stack));
			if (num > 0)
			{
				view.RemoveItem(item, num);
			}
			return num;
		}

		public static bool PutIntoView(Inventory view, Inventory from, ItemData item, int amount, int x, int y)
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: 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_00da: 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)
			amount = Mathf.Min(amount, item.m_stack);
			if (amount <= 0)
			{
				return false;
			}
			string key = Catalog.Key(item);
			ItemData val = (Catalog.Stackable(item) ? ((IEnumerable<ItemData>)view.m_inventory).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => i != item && Catalog.Stackable(i) && Catalog.Key(i) == key)) : null);
			if (val != null)
			{
				val.m_stack += amount;
			}
			else
			{
				Vector2i val2 = (Vector2i)((x >= 0 && y >= 0 && x < view.m_width && y < view.m_height && view.GetItemAt(x, y) == null) ? new Vector2i(x, y) : view.FindEmptySlot(false));
				if (val2.x < 0)
				{
					return false;
				}
				ItemData val3 = item.Clone();
				val3.m_stack = amount;
				val3.m_equipped = false;
				val3.m_gridPos = val2;
				view.m_inventory.Add(val3);
			}
			ItemData obj = item;
			obj.m_stack -= amount;
			if (item.m_stack <= 0)
			{
				from.RemoveItem(item);
			}
			else
			{
				Catalog.NotifyChanged(from);
			}
			Catalog.NotifyChanged(view);
			return true;
		}
	}
	[HarmonyPatch(typeof(Inventory), "MoveItemToThis", new Type[]
	{
		typeof(Inventory),
		typeof(ItemData)
	})]
	internal static class MoveOutPatch
	{
		[HarmonyPriority(600)]
		private static bool Prefix(Inventory __instance, Inventory fromInventory, ItemData item)
		{
			if (!Stacks.Active || __instance == fromInventory || !TerminalSession.IsView(fromInventory) || !Stacks.Oversized(item))
			{
				return true;
			}
			Stacks.TakeStack(__instance, fromInventory, item);
			return false;
		}
	}
	[HarmonyPatch(typeof(Inventory), "MoveItemToThis", new Type[]
	{
		typeof(Inventory),
		typeof(ItemData),
		typeof(int),
		typeof(int),
		typeof(int)
	})]
	internal static class MoveAtPatch
	{
		[HarmonyPriority(600)]
		private static bool Prefix(Inventory __instance, Inventory fromInventory, ItemData item, ref int amount, int x, int y, ref bool __result)
		{
			if (!Stacks.Active || item == null || __instance == fromInventory)
			{
				return true;
			}
			if (TerminalSession.IsView(fromInventory))
			{
				if (Catalog.Stackable(item))
				{
					amount = Mathf.Min(amount, item.m_shared.m_maxStackSize);
				}
				return true;
			}
			if (!TerminalSession.IsView(__instance))
			{
				return true;
			}
			__result = Stacks.PutIntoView(__instance, fromInventory, item, amount, x, y);
			return false;
		}
	}
	[HarmonyPatch(typeof(Inventory), "MoveAll")]
	internal static class MoveAllPatch
	{
		[HarmonyPriority(600)]
		private static bool Prefix(Inventory __instance, Inventory fromInventory)
		{
			if (!Stacks.Active || __instance == fromInventory || !TerminalSession.IsView(fromInventory))
			{
				return true;
			}
			foreach (ItemData item in fromInventory.GetAllItems().ToList())
			{
				if (Catalog.Stackable(item))
				{
					while (fromInventory.ContainsItem(item) && Stacks.TakeStack(__instance, fromInventory, item) > 0)
					{
					}
				}
				else if (__instance.AddItem(item))
				{
					fromInventory.RemoveItem(item);
				}
			}
			Catalog.NotifyChanged(__instance);
			Catalog.NotifyChanged(fromInventory);
			return false;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "SetupDragItem")]
	internal static class DragPatch
	{
		private static void Prefix(ItemData item, Inventory inventory, ref int amount)
		{
			if (Stacks.Active && item != null && Catalog.Stackable(item) && TerminalSession.IsView(inventory))
			{
				amount = Mathf.Min(amount, item.m_shared.m_maxStackSize);
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "ShowSplitDialog")]
	internal static class SplitPatch
	{
		private static void Postfix(InventoryGui __instance, object[] __args)
		{
			object obj = __args[0];
			ItemData val = (ItemData)((obj is ItemData) ? obj : null);
			if (Stacks.Active && Stacks.Oversized(val) && TerminalSession.IsView((Inventory)/*isinst with value type is only supported in some contexts*/))
			{
				bool flag = ZInput.GetKey((KeyCode)306, true) || ZInput.GetKey((KeyCode)305, true);
				object obj2 = AccessTools.Field(typeof(InventoryGui), "m_splitDialog")?.GetValue(__instance);
				((obj2 == null) ? null : AccessTools.Method(obj2.GetType(), "UpdateLimits", new Type[2]
				{
					typeof(int),
					typeof(bool)
				}, (Type[])null))?.Invoke(obj2, new object[2]
				{
					val.m_shared.m_maxStackSize,
					flag
				});
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "DropItem")]
	internal static class DropPatch
	{
		private static void Prefix(Inventory inventory, ItemData item, ref int amount)
		{
			if (Stacks.Active && item != null && Catalog.Stackable(item) && TerminalSession.IsView(inventory))
			{
				amount = Mathf.Min(amount, item.m_shared.m_maxStackSize);
			}
		}
	}
	internal static class TerminalModel
	{
		private static readonly Color GlowColor = new Color(0.35f, 0.75f, 1f);

		private static readonly Dictionary<string, Material> Cache = new Dictionary<string, Material>();

		public static void Build(GameObject prefab)
		{
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Expected O, but got Unknown
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0331: Unknown result type (might be due to invalid IL or missing references)
			//IL_0336: Unknown result type (might be due to invalid IL or missing references)
			//IL_034a: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0400: Unknown result type (might be due to invalid IL or missing references)
			//IL_0414: Unknown result type (might be due to invalid IL or missing references)
			//IL_0432: Unknown result type (might be due to invalid IL or missing references)
			//IL_0446: Unknown result type (might be due to invalid IL or missing references)
			//IL_0453: Unknown result type (might be due to invalid IL or missing references)
			//IL_0458: Unknown result type (might be due to invalid IL or missing references)
			//IL_046c: Unknown result type (might be due to invalid IL or missing references)
			//IL_048a: Unknown result type (might be due to invalid IL or missing references)
			//IL_048f: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0511: Unknown result type (might be due to invalid IL or missing references)
			//IL_0530: Unknown result type (might be due to invalid IL or missing references)
			//IL_0544: Unknown result type (might be due to invalid IL or missing references)
			//IL_0558: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0618: Unknown result type (might be due to invalid IL or missing references)
			//IL_0631: Unknown result type (might be due to invalid IL or missing references)
			//IL_0576: Unknown result type (might be due to invalid IL or missing references)
			//IL_057b: Unknown result type (might be due to invalid IL or missing references)
			//IL_058e: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ba: Unknown result type (might be due to invalid IL or missing references)
			List<Material> source = (from m in prefab.GetComponentsInChildren<Renderer>(true).SelectMany((Renderer r) => r.sharedMaterials)
				where Object.op_Implicit((Object)(object)m)
				select m).Distinct().ToList();
			Material val = ((IEnumerable<Material>)source).FirstOrDefault((Func<Material, bool>)((Material m) => ((Object)m).name.ToLowerInvariant().Contains("wood"))) ?? source.FirstOrDefault();
			Material source2 = ((IEnumerable<Material>)source).FirstOrDefault((Func<Material, bool>)delegate(Material m)
			{
				string text = ((Object)m).name.ToLowerInvariant();
				return text.Contains("metal") || text.Contains("iron") || text.Contains("black");
			}) ?? val;
			if (!Object.op_Implicit((Object)(object)val))
			{
				Plugin.Log.LogWarning((object)"No base material found, keeping the base chest model");
				return;
			}
			foreach (Transform item in ((IEnumerable)prefab.transform).Cast<Transform>().ToList())
			{
				Object.DestroyImmediate((Object)(object)((Component)item).gameObject);
			}
			Collider[] components = prefab.GetComponents<Collider>();
			for (int num = 0; num < components.Length; num++)
			{
				Object.DestroyImmediate((Object)(object)components[num]);
			}
			Renderer[] components2 = prefab.GetComponents<Renderer>();
			for (int num = 0; num < components2.Length; num++)
			{
				Object.DestroyImmediate((Object)(object)components2[num]);
			}
			MeshFilter[] components3 = prefab.GetComponents<MeshFilter>();
			for (int num = 0; num < components3.Length; num++)
			{
				Object.DestroyImmediate((Object)(object)components3[num]);
			}
			LODGroup[] components4 = prefab.GetComponents<LODGroup>();
			for (int num = 0; num < components4.Length; num++)
			{
				Object.DestroyImmediate((Object)(object)components4[num]);
			}
			int num2 = LayerMask.NameToLayer("piece");
			if (num2 < 0)
			{
				num2 = prefab.layer;
			}
			GameObject val2 = new GameObject("terminal_model");
			val2.transform.SetParent(prefab.transform, false);
			val2.layer = num2;
			Material mat = Tinted(val, new Color(0.62f, 0.5f, 0.42f), "terminal_wood");
			Material mat2 = Tinted(source2, new Color(0.38f, 0.4f, 0.46f), "terminal_metal");
			Material mat3 = Glowing(source2, GlowColor, "terminal_glow");
			Shape(val2, (PrimitiveType)3, new Vector3(0f, 0.1f, 0f), Vector3.zero, new Vector3(1.3f, 0.2f, 0.85f), mat2, num2);
			Shape(val2, (PrimitiveType)3, new Vector3(0f, 0.5f, 0f), Vector3.zero, new Vector3(1.1f, 0.6f, 0.7f), mat, num2);
			Shape(val2, (PrimitiveType)3, new Vector3(0f, 0.62f, 0f), Vector3.zero, new Vector3(1.16f, 0.07f, 0.76f), mat2, num2);
			float[] array = new float[2] { -0.55f, 0.55f };
			foreach (float num3 in array)
			{
				float[] array2 = new float[2] { -0.35f, 0.35f };
				foreach (float num5 in array2)
				{
					Shape(val2, (PrimitiveType)3, new Vector3(num3, 0.5f, num5), Vector3.zero, new Vector3(0.12f, 0.82f, 0.12f), mat2, num2);
				}
			}
			array = new float[2] { 0.36f, 0.48f };
			foreach (float num6 in array)
			{
				Shape(val2, (PrimitiveType)3, new Vector3(0f, num6, -0.355f), Vector3.zero, new Vector3(0.7f, 0.035f, 0.02f), mat3, num2);
			}
			Shape(val2, (PrimitiveType)3, new Vector3(0f, 0.42f, -0.355f), new Vector3(0f, 0f, 45f), new Vector3(0.1f, 0.1f, 0.02f), mat3, num2);
			GameObject parent = Holder(val2, new Vector3(0f, 0.95f, -0.05f), new Vector3(-22f, 0f, 0f), num2);
			Shape(parent, (PrimitiveType)3, Vector3.zero, Vector3.zero, new Vector3(1.2f, 0.08f, 0.82f), mat, num2);
			Shape(parent, (PrimitiveType)3, new Vector3(0f, 0.045f, 0f), Vector3.zero, new Vector3(0.86f, 0.02f, 0.52f), mat3, num2);
			Shape(parent, (PrimitiveType)3, new Vector3(0f, 0.03f, 0f), Vector3.zero, new Vector3(0.94f, 0.03f, 0.6f), mat2, num2);
			Shape(val2, (PrimitiveType)2, new Vector3(0f, 1.18f, 0.3f), Vector3.zero, new Vector3(0.08f, 0.16f, 0.08f), mat2, num2);
			Shape(val2, (PrimitiveType)3, new Vector3(0f, 1.5f, 0.3f), new Vector3(45f, 0f, 35f), new Vector3(0.17f, 0.17f, 0.17f), mat3, num2);
			if (Plugin.Glow.Value)
			{
				GameObject val3 = new GameObject("terminal_light");
				val3.transform.SetParent(val2.transform, false);
				val3.transform.localPosition = new Vector3(0f, 1.5f, 0.3f);
				Light obj = val3.AddComponent<Light>();
				obj.type = (LightType)2;
				obj.color = GlowColor;
				obj.range = 3.5f;
				obj.intensity = 1.3f;
				obj.shadows = (LightShadows)0;
			}
			GameObject val4 = new GameObject("terminal_collider")
			{
				layer = num2
			};
			val4.transform.SetParent(prefab.transform, false);
			BoxCollider obj2 = val4.AddComponent<BoxCollider>();
			obj2.center = new Vector3(0f, 0.7f, 0f);
			obj2.size = new Vector3(1.3f, 1.4f, 0.85f);
			WearNTear component = prefab.GetComponent<WearNTear>();
			if (Object.op_Implicit((Object)(object)component))
			{
				component.m_new = val2;
				component.m_worn = val2;
				component.m_broken = val2;
				component.m_wet = null;
				component.m_fragmentRoots = (GameObject[])(object)new GameObject[1] { val2 };
			}
			Container component2 = prefab.GetComponent<Container>();
			if (Object.op_Implicit((Object)(object)component2))
			{
				component2.m_open = null;
				component2.m_closed = null;
			}
		}

		private static GameObject Holder(GameObject parent, Vector3 pos, Vector3 euler, int layer)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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_003c: Expected O, but got Unknown
			GameObject val = new GameObject("part")
			{
				layer = layer
			};
			val.transform.SetParent(parent.transform, false);
			val.transform.localPosition = pos;
			val.transform.localEulerAngles = euler;
			return val;
		}

		private unsafe static void Shape(GameObject parent, PrimitiveType type, Vector3 pos, Vector3 euler, Vector3 size, Material mat, int layer)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Holder(parent, pos, euler, layer);
			GameObject obj = GameObject.CreatePrimitive(type);
			((Object)obj).name = ((object)(*(PrimitiveType*)(&type))/*cast due to .constrained prefix*/).ToString().ToLowerInvariant();
			obj.layer = layer;
			Collider component = obj.GetComponent<Collider>();
			if (Object.op_Implicit((Object)(object)component))
			{
				Object.DestroyImmediate((Object)(object)component);
			}
			obj.transform.SetParent(val.transform, false);
			obj.transform.localScale = size;
			obj.GetComponent<Renderer>().sharedMaterial = mat;
		}

		private static Material Tinted(Material source, Color tint, string name)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (Cache.TryGetValue(name, out var value))
			{
				return value;
			}
			Material val = new Material(source)
			{
				name = name
			};
			if (val.HasProperty("_Color"))
			{
				val.color = tint;
			}
			Cache[name] = val;
			return val;
		}

		private static Material Glowing(Material source, Color color, string name)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_002d: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			if (Cache.TryGetValue(name, out var value))
			{
				return value;
			}
			Material val = new Material(source)
			{
				name = name
			};
			if (val.HasProperty("_Color"))
			{
				val.color = color;
			}
			if (val.HasProperty("_EmissionColor"))
			{
				val.EnableKeyword("_EMISSION");
				val.SetColor("_EmissionColor", color * 2f);
			}
			Cache[name] = val;
			return val;
		}
	}
	public class TerminalSession : MonoBehaviour
	{
		private class ViewEntry
		{
			public ItemData Item;

			public Source? Source;
		}

		private struct Source
		{
			public Container Container;

			public ItemData Item;
		}

		private const int MinRows = 4;

		private int _viewWidth = 8;

		private PanelBar _panelBar;

		private float _panelDeadline;

		private bool _active;

		private bool _shown;

		private float _startTime;

		private bool _syncing;

		private bool _dirty;

		private Container _terminal;

		private Inventory _view;

		private List<Container> _chests = new List<Container>();

		private Dictionary<string, int> _counts = new Dictionary<string, int>();

		private readonly Dictionary<ItemData, Source> _unique = new Dictionary<ItemData, Source>();

		private string _search = "";

		private Category _category;

		private SearchBar _bar;

		public static TerminalSession Instance { get; private set; }

		public static bool SearchFocused { get; private set; }

		private bool UsePanelBar
		{
			get
			{
				if (_panelBar == null)
				{
					return Time.unscaledTime <= _panelDeadline;
				}
				return true;
			}
		}

		private void Awake()
		{
			Instance = this;
		}

		public static bool IsViewing(Container c)
		{
			if (Object.op_Implicit((Object)(object)Instance) && Instance._active)
			{
				return (Object)(object)Instance._terminal == (Object)(object)c;
			}
			return false;
		}

		public static bool IsView(Inventory inv)
		{
			if (inv != null && Object.op_Implicit((Object)(object)Instance) && Instance._active)
			{
				return Instance._view == inv;
			}
			return false;
		}

		public void Begin(Container terminal)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			End();
			Inventory val = (Object.op_Implicit((Object)(object)terminal) ? terminal.GetInventory() : null);
			if (val == null)
			{
				return;
			}
			_active = true;
			_shown = false;
			_viewWidth = Plugin.Columns.Value;
			_search = "";
			_category = Category.All;
			_startTime = Time.unscaledTime;
			_terminal = terminal;
			_view = val;
			_chests = Catalog.FindContainers(((Component)terminal).transform.position, Plugin.Radius.Value);
			foreach (Container chest in _chests)
			{
				Inventory inventory = chest.GetInventory();
				inventory.m_onChanged = (Action)Delegate.Combine(inventory.m_onChanged, new Action(OnRealChanged));
			}
			Inventory view = _view;
			view.m_onChanged = (Action)Delegate.Combine(view.m_onChanged, new Action(OnViewChanged));
			try
			{
				Rebuild();
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Terminal failed to read chests: {arg}");
				End();
				return;
			}
			Plugin.Log.LogInfo((object)$"Terminal opened: {_chests.Count} chests in range");
		}

		public void End()
		{
			if (!_active)
			{
				return;
			}
			_active = false;
			SearchFocused = false;
			_panelBar?.Destroy();
			_panelBar = null;
			foreach (Container chest in _chests)
			{
				Inventory val = (Object.op_Implicit((Object)(object)chest) ? chest.GetInventory() : null);
				if (val != null)
				{
					val.m_onChanged = (Action)Delegate.Remove(val.m_onChanged, new Action(OnRealChanged));
				}
			}
			_chests.Clear();
			if (_view != null)
			{
				Inventory view = _view;
				view.m_onChanged = (Action)Delegate.Remove(view.m_onChanged, new Action(OnViewChanged));
				_syncing = true;
				try
				{
					_view.m_inventory.Clear();
					Catalog.NotifyChanged(_view);
				}
				catch (Exception arg)
				{
					Plugin.Log.LogError((object)$"Terminal failed to clear its view: {arg}");
				}
				finally
				{
					_syncing = false;
				}
			}
			_view = null;
			_terminal = null;
			_counts.Clear();
			_unique.Clear();
			Plugin.Log.LogInfo((object)"Terminal closed");
		}

		private void OnDestroy()
		{
			End();
		}

		private void Update()
		{
			if (!_active)
			{
				return;
			}
			InventoryGui instance = InventoryGui.instance;
			if (Object.op_Implicit((Object)(object)_terminal) && Object.op_Implicit((Object)(object)instance) && InventoryGui.IsVisible() && (Object)(object)instance.m_currentContainer == (Object)(object)_terminal)
			{
				if (!_shown)
				{
					_panelDeadline = Time.unscaledTime + 1f;
				}
				_shown = true;
			}
			else if (_shown || !Object.op_Implicit((Object)(object)_terminal) || Time.unscaledTime - _startTime > 3f)
			{
				End();
				return;
			}
			if (!_dirty || Object.op_Implicit((Object)(object)instance.m_dragGo))
			{
				return;
			}
			try
			{
				Rebuild();
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Terminal refresh failed: {arg}");
				End();
			}
		}

		private void LateUpdate()
		{
			if (!_active || !_shown)
			{
				return;
			}
			if (_panelBar == null && Time.unscaledTime <= _panelDeadline)
			{
				try
				{
					_panelBar = PanelBar.TryCreate(OnSearchChanged, OnCategoryClicked);
					if (_panelBar != null)
					{
						Plugin.Log.LogInfo((object)"Terminal search placed inside the SeneaL UI chest panel");
					}
				}
				catch (Exception arg)
				{
					Plugin.Log.LogWarning((object)$"Could not build the SeneaL UI search bar, using the fallback bar: {arg}");
					_panelDeadline = 0f;
				}
			}
			if (_panelBar == null)
			{
				return;
			}
			try
			{
				_panelBar.Tick(_category);
				SearchFocused = _panelBar.Focused;
			}
			catch (Exception arg2)
			{
				Plugin.Log.LogWarning((object)$"SeneaL UI search bar failed, using the fallback bar: {arg2}");
				_panelBar.Destroy();
				_panelBar = null;
				_panelDeadline = 0f;
				SearchFocused = false;
			}
		}

		private void OnSearchChanged(string text)
		{
			_search = text ?? "";
			_dirty = true;
		}

		private void OnCategoryClicked(int index)
		{
			_category = (Category)index;
			_dirty = true;
		}

		private void OnRealChanged()
		{
			if (!_syncing)
			{
				_dirty = true;
			}
		}

		private void OnViewChanged()
		{
			if (_syncing || !_active)
			{
				return;
			}
			try
			{
				Sync();
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Terminal sync failed: {arg}");
				_dirty = true;
			}
		}

		private void Rebuild()
		{
			//IL_0312: Unknown result type (might be due to invalid IL or missing references)
			//IL_0317: Unknown result type (might be due to invalid IL or missing references)
			_dirty = false;
			if (!_active || _view == null)
			{
				return;
			}
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			Dictionary<string, ItemData> dictionary2 = new Dictionary<string, ItemData>();
			List<Source> list = new List<Source>();
			_chests.RemoveAll((Container c) => !Object.op_Implicit((Object)(object)c) || c.GetInventory() == null);
			foreach (Container chest in _chests)
			{
				foreach (ItemData allItem in chest.GetInventory().GetAllItems())
				{
					if (allItem?.m_shared == null || !Matches(allItem))
					{
						continue;
					}
					if (Catalog.Stackable(allItem))
					{
						string key = Catalog.Key(allItem);
						dictionary.TryGetValue(key, out var value);
						dictionary[key] = value + allItem.m_stack;
						if (!dictionary2.ContainsKey(key))
						{
							dictionary2[key] = allItem;
						}
					}
					else
					{
						list.Add(new Source
						{
							Container = chest,
							Item = allItem
						});
					}
				}
			}
			List<ViewEntry> list2 = new List<ViewEntry>();
			foreach (KeyValuePair<string, int> item2 in dictionary)
			{
				ItemData val = dictionary2[item2.Key];
				int num = item2.Value;
				int num2 = (Plugin.MergeStacks.Value ? int.MaxValue : Mathf.Max(1, val.m_shared.m_maxStackSize));
				while (num > 0)
				{
					int num3 = Mathf.Min(num, num2);
					list2.Add(new ViewEntry
					{
						Item = Copy(val, num3)
					});
					num -= num3;
				}
			}
			foreach (Source item3 in list)
			{
				list2.Add(new ViewEntry
				{
					Item = Copy(item3.Item, item3.Item.m_stack),
					Source = item3
				});
			}
			List<ViewEntry> list3 = (from e in list2
				orderby Catalog.CategoryOf(e.Item), Localization.instance.Localize(e.Item.m_shared.m_name), e.Item.m_quality descending
				select e).ToList();
			_syncing = true;
			try
			{
				_view.m_inventory.Clear();
				_unique.Clear();
				for (int num4 = 0; num4 < list3.Count; num4++)
				{
					ItemData item = list3[num4].Item;
					item.m_gridPos = new Vector2i(num4 % _viewWidth, num4 / _viewWidth);
					_view.m_inventory.Add(item);
					if (list3[num4].Source.HasValue)
					{
						_unique[item] = list3[num4].Source.Value;
					}
				}
				int num5 = Mathf.Max(4, (list3.Count + _viewWidth - 1) / _viewWidth + 1);
				_view.m_width = _viewWidth;
				_view.m_height = Mathf.Max(num5, _shown ? _view.m_height : 0);
				_counts = CountStacks(_view);
				Catalog.NotifyChanged(_view);
			}
			finally
			{
				_syncing = false;
			}
		}

		private bool Matches(ItemData item)
		{
			if (_category != Category.All && Catalog.CategoryOf(item) != _category)
			{
				return false;
			}
			string text = _search.Trim().ToLowerInvariant();
			if (text.Length != 0)
			{
				return Catalog.SearchText(item).Contains(text);
			}
			return true;
		}

		private static ItemData Copy(ItemData item, int stack)
		{
			ItemData obj = item.Clone();
			obj.m_stack = stack;
			obj.m_equipped = false;
			return obj;
		}

		private static Dictionary<string, int> CountStacks(Inventory inv)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (ItemData item in inv.m_inventory)
			{
				if (Catalog.Stackable(item))
				{
					string key = Catalog.Key(item);
					dictionary.TryGetValue(key, out var value);
					dictionary[key] = value + item.m_stack;
				}
			}
			return dictionary;
		}

		private void Sync()
		{
			_syncing = true;
			try
			{
				Dictionary<string, int> dictionary = CountStacks(_view);
				Dictionary<string, ItemData> dictionary2 = new Dictionary<string, ItemData>();
				HashSet<ItemData> hashSet = new HashSet<ItemData>();
				foreach (ItemData item in _view.m_inventory)
				{
					if (Catalog.Stackable(item))
					{
						dictionary2[Catalog.Key(item)] = item;
					}
					else
					{
						hashSet.Add(item);
					}
				}
				foreach (string item2 in _counts.Keys.Union(dictionary.Keys).ToList())
				{
					_counts.TryGetValue(item2, out var value);
					dictionary.TryGetValue(item2, out var value2);
					if (value2 < value)
					{
						RemoveFromChests(item2, value - value2);
					}
					else if (value2 > value)
					{
						ItemData val = dictionary2[item2];
						int num = AddToChests(val, value2 - value);
						if (num > 0)
						{
							ReturnToPlayer(val, num);
						}
					}
				}
				foreach (KeyValuePair<ItemData, Source> item3 in _unique.ToList())
				{
					if (!hashSet.Contains(item3.Key))
					{
						Source value3 = item3.Value;
						Inventory val2 = (Object.op_Implicit((Object)(object)value3.Container) ? value3.Container.GetInventory() : null);
						if (val2 != null && val2.ContainsItem(value3.Item) && Catalog.EnsureOwner(value3.Container))
						{
							val2.RemoveItem(value3.Item);
						}
						else
						{
							Plugin.Log.LogWarning((object)("Terminal: " + value3.Item.m_shared.m_name + " was already gone from its chest"));
						}
						_unique.Remove(item3.Key);
					}
				}
				foreach (ItemData item4 in hashSet)
				{
					if (!_unique.ContainsKey(item4))
					{
						if (AddUniqueToChests(item4, out var source))
						{
							_unique[item4] = source;
						}
						else
						{
							ReturnToPlayer(item4, item4.m_stack);
						}
					}
				}
				if (MergeDuplicates())
				{
					Catalog.NotifyChanged(_view);
				}
				_counts = CountStacks(_view);
				if (_view.GetEmptySlots() < _viewWidth)
				{
					Inventory view = _view;
					view.m_height++;
				}
			}
			finally
			{
				_syncing = false;
			}
		}

		private bool MergeDuplicates()
		{
			if (!Plugin.MergeStacks.Value)
			{
				return false;
			}
			bool result = false;
			Dictionary<string, ItemData> dictionary = new Dictionary<string, ItemData>();
			foreach (ItemData item in _view.m_inventory.ToList())
			{
				if (Catalog.Stackable(item))
				{
					string key = Catalog.Key(item);
					if (dictionary.TryGetValue(key, out var value))
					{
						ItemData obj = value;
						obj.m_stack += item.m_stack;
						_view.m_inventory.Remove(item);
						result = true;
					}
					else
					{
						dictionary[key] = item;
					}
				}
			}
			return result;
		}

		private void RemoveFromChests(string key, int amount)
		{
			foreach (Container chest in _chests)
			{
				if (amount <= 0)
				{
					return;
				}
				Inventory val = (Object.op_Implicit((Object)(object)chest) ? chest.GetInventory() : null);
				if (val == null)
				{
					continue;
				}
				foreach (ItemData item in val.GetAllItems().ToList())
				{
					if (amount <= 0)
					{
						break;
					}
					if (Catalog.Stackable(item) && !(Catalog.Key(item) != key) && Catalog.EnsureOwner(chest))
					{
						int num = Mathf.Min(item.m_stack, amount);
						val.RemoveItem(item, num);
						amount -= num;
					}
				}
			}
			if (amount > 0)
			{
				Plugin.Log.LogWarning((object)$"Terminal: {amount} x {key} were already gone from the chests");
			}
		}

		private int AddToChests(ItemData template, int amount)
		{
			string key = Catalog.Key(template);
			foreach (Container item in from c in _chests
				where Object.op_Implicit((Object)(object)c) && c.GetInventory() != null
				orderby c.GetInventory().GetAllItems().Any((ItemData i) => Catalog.Key(i) == key) descending
				select c)
			{
				if (amount <= 0)
				{
					break;
				}
				Inventory inventory = item.GetInventory();
				int num = Catalog.MaxFit(inventory, template, amount);
				if (num > 0 && Catalog.EnsureOwner(item) && inventory.AddItem(Copy(template, num)))
				{
					amount -= num;
				}
			}
			return amount;
		}

		private bool AddUniqueToChests(ItemData item, out Source source)
		{
			foreach (Container chest in _chests)
			{
				Inventory val = (Object.op_Implicit((Object)(object)chest) ? chest.GetInventory() : null);
				if (val != null && val.HaveEmptySlot() && Catalog.EnsureOwner(chest))
				{
					ItemData val2 = Copy(item, item.m_stack);
					if (val.AddItem(val2))
					{
						source = new Source
						{
							Container = chest,
							Item = val2
						};
						return true;
					}
				}
			}
			source = default(Source);
			return false;
		}

		private void ReturnToPlayer(ItemData viewItem, int amount)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			ItemData val = Copy(viewItem, amount);
			_view.RemoveItem(viewItem, amount);
			if (Object.op_Implicit((Object)(object)localPlayer) && ((Humanoid)localPlayer).GetInventory().CanAddItem(val, amount))
			{
				((Humanoid)localPlayer).GetInventory().AddItem(val);
			}
			else if (Object.op_Implicit((Object)(object)localPlayer))
			{
				ItemDrop.DropItem(val, amount, ((Component)localPlayer).transform.position + ((Component)localPlayer).transform.forward + Vector3.up, Quaternion.identity);
			}
			Message(Loc.T("chests_full"));
		}

		private static void Message(string text)
		{
			Catalog.ShowMessage((MessageType)2, text);
		}

		private void OnGUI()
		{
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			if (!_active || !_shown)
			{
				SearchFocused = false;
			}
			else
			{
				if (UsePanelBar)
				{
					return;
				}
				_bar = _bar ?? new SearchBar();
				try
				{
					SearchBar.Result result = _bar.Draw(_search, _category, _chests.Count);
					SearchFocused = result.Focused;
					if (result.Search != _search || result.Category != _category)
					{
						_search = result.Search;
						_category = result.Category;
						_dirty = true;
					}
				}
				catch (Exception arg)
				{
					Plugin.Log.LogError((object)$"Terminal search bar failed: {arg}");
					SearchFocused = false;
					GUI.matrix = Matrix4x4.identity;
				}
			}
		}
	}
}