Decompiled source of TidyChests v1.0.1

plugins/TidyChests/TidyChests.dll

Decompiled 2 days 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.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using TidyChests.Compat;
using TidyChests.Containers;
using TidyChests.Find;
using TidyChests.Hud;
using TidyChests.Stash;
using TidyChests.Ui;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("Egor Fadeev")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Terraria-style quick stack for Valheim: a Stash button moves your materials into the nearby chests that already hold them, and a hotkey highlights the chests that contain the item under the cursor.")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1+24a6f35d5636a7563caa38cb7a2d70a06d3d7fd5")]
[assembly: AssemblyProduct("Tidy Chests")]
[assembly: AssemblyTitle("TidyChests")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 TidyChests
{
	public sealed class ModConfig
	{
		private static readonly string[] ItemTypeNames = Enum.GetNames(typeof(ItemType));

		public const string DefaultItemTypes = "Material, Consumable, Ammo, AmmoNonEquipable, Trophy, Misc, Fish";

		private readonly ConfigFile _config;

		public ConfigEntry<bool> Enabled { get; }

		public ConfigEntry<bool> IsDebug { get; }

		public ConfigEntry<float> Radius { get; }

		public ConfigEntry<bool> IncludeHotbar { get; }

		public ConfigEntry<string> ItemTypes { get; }

		public ConfigEntry<string> Blacklist { get; }

		public ConfigEntry<bool> ShowMessage { get; }

		public ConfigEntry<KeyboardShortcut> FindKey { get; }

		public ConfigEntry<float> HighlightDuration { get; }

		public ConfigEntry<bool> ScreenMarker { get; }

		public ConfigEntry<bool> Light { get; }

		public ConfigEntry<bool> Glow { get; }

		public ConfigEntry<bool> CloseInventory { get; }

		public ConfigEntry<bool> ShowButton { get; }

		public ConfigEntry<Vector2> ButtonOffset { get; }

		public ConfigEntry<Vector2> ButtonSize { get; }

		public ModConfig(ConfigFile config)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Expected O, but got Unknown
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			_config = config;
			string text = string.Join(", ", ItemTypeNames);
			Enabled = config.Bind<bool>("General", "Enabled", true, "Enable this mod: the Stash button and the find key.");
			IsDebug = config.Bind<bool>("General", "IsDebug", false, "Log every item decision and every chest that is considered when stashing.");
			Radius = config.Bind<float>("Stash", "Radius", 10f, new ConfigDescription("Chests within this many metres of the player are used, both by the Stash button and by the find key.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
			IncludeHotbar = config.Bind<bool>("Stash", "IncludeHotbar", false, "Also stash items from the first inventory row (the hotbar).");
			ItemTypes = config.Bind<string>("Stash", "ItemTypes", "Material, Consumable, Ammo, AmmoNonEquipable, Trophy, Misc, Fish", "Item types that may be stashed (comma-separated). Equipment, tools and weapons are left out by default. Valid types: " + text);
			Blacklist = config.Bind<string>("Stash", "Blacklist", "", "Items that are never stashed (comma-separated). Use prefab names or item names, for example: Wood, $item_coal, Resin");
			ShowMessage = config.Bind<bool>("Stash", "ShowMessage", true, "Show a message with the result after stashing.");
			FindKey = config.Bind<KeyboardShortcut>("Find", "FindKey", new KeyboardShortcut((KeyCode)116, Array.Empty<KeyCode>()), "With the inventory open, point at an item (in the inventory, or an ingredient or recipe in the crafting panel) and press this key: the inventory closes and every chest in range that holds the item is highlighted. Modifiers are allowed, e.g. \"T + LeftControl\".");
			HighlightDuration = config.Bind<float>("Find", "HighlightDuration", 8f, new ConfigDescription("Seconds the found chests stay highlighted.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f), Array.Empty<object>()));
			ScreenMarker = config.Bind<bool>("Find", "ScreenMarker", true, "Draw an arrow with the item count and the distance over each found chest (at the screen edge when the chest is out of view).");
			Light = config.Bind<bool>("Find", "Light", true, "Light up each found chest with a pulsing light.");
			Glow = config.Bind<bool>("Find", "Glow", true, "Tint each found chest with an emissive glow. Only works for materials whose shader has an emission colour.");
			CloseInventory = config.Bind<bool>("Find", "CloseInventory", true, "Close the inventory when chests are found, so the highlights are visible right away. When nothing is found the inventory stays open.");
			ShowButton = config.Bind<bool>("Button", "ShowButton", true, "Show the Stash button in the inventory. The console command 'tidychests stash' works without it.");
			ButtonOffset = config.Bind<Vector2>("Button", "ButtonOffset", new Vector2(41f, -56f), "Position of the Stash button relative to the weight display of the inventory panel, in UI pixels (x right, y up).");
			ButtonSize = config.Bind<Vector2>("Button", "ButtonSize", new Vector2(120f, 38f), "Width and height of the Stash button in UI pixels.");
		}

		public void Reload()
		{
			_config.Reload();
		}

		public StashRules BuildRules()
		{
			return StashRules.Parse(new StashRuleSettings
			{
				IncludeHotbar = IncludeHotbar.Value,
				ItemTypes = ItemTypes.Value,
				Blacklist = Blacklist.Value
			}, ItemTypeNames, delegate(string warning)
			{
				Plugin.Log.LogWarning((object)warning);
			});
		}

		public HighlightOptions ToHighlightOptions()
		{
			return new HighlightOptions(HighlightDuration.Value, Light.Value, Glow.Value);
		}

		public string Describe()
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			return (Enabled.Value ? "enabled" : "disabled") + ", radius " + Radius.Value.ToString("0.#", invariantCulture) + " m, hotbar " + (IncludeHotbar.Value ? "included" : "excluded") + ", " + BuildRules().Describe() + ", " + string.Format("find key {0}, highlight {1} s, ", FindKey.Value, HighlightDuration.Value.ToString("0.#", invariantCulture)) + "button " + (ShowButton.Value ? "shown" : "hidden");
		}
	}
	[BepInPlugin("muindor.TidyChests", "Tidy Chests", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		private Harmony? _harmony;

		public static ManualLogSource Log { get; private set; }

		public static ModConfig Settings { get; private set; }

		public static ChestFinder? Finder { get; private set; }

		public static bool Enabled
		{
			get
			{
				if (Settings != null)
				{
					return Settings.Enabled.Value;
				}
				return false;
			}
		}

		private void Awake()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Settings = new ModConfig(((BaseUnityPlugin)this).Config);
			Finder = ((Component)this).gameObject.AddComponent<ChestFinder>();
			_harmony = new Harmony("muindor.TidyChests");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			Translations.ApplyIfLoaded();
			Log.LogInfo((object)("Tidy Chests 1.0.1 loaded: " + Settings.Describe()));
			SlotMods.Report();
			ChestMods.Report();
		}

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

		public static void Debug(string message)
		{
			if (Settings != null && Settings.IsDebug.Value)
			{
				Log.LogInfo((object)message);
			}
		}
	}
	internal static class Translations
	{
		public const string Button = "tidychests_button";

		public const string Stashed = "tidychests_stashed";

		public const string Nothing = "tidychests_nothing";

		public const string NoChests = "tidychests_no_chests";

		public const string Found = "tidychests_found";

		public const string NotFound = "tidychests_not_found";

		public const string Hover = "tidychests_hover";

		private const string DefaultLanguage = "English";

		private static readonly Dictionary<string, Dictionary<string, string>> Words = new Dictionary<string, Dictionary<string, string>>
		{
			["English"] = new Dictionary<string, string>
			{
				["tidychests_button"] = "Stash",
				["tidychests_stashed"] = "Stashed {0} items into {1} chests",
				["tidychests_nothing"] = "Nothing to stash: no chest nearby holds these items",
				["tidychests_no_chests"] = "No usable chest within {0} m",
				["tidychests_found"] = "{0}: found in {1} chests",
				["tidychests_not_found"] = "{0}: not in any chest within {1} m",
				["tidychests_hover"] = "Point at an item in the inventory or the crafting panel and press {0} to find it in nearby chests"
			},
			["Russian"] = new Dictionary<string, string>
			{
				["tidychests_button"] = "Убрать",
				["tidychests_stashed"] = "Убрано предметов: {0}, в сундуков: {1}",
				["tidychests_nothing"] = "Нечего убирать: поблизости нет сундуков с такими предметами",
				["tidychests_no_chests"] = "В радиусе {0} м нет подходящих сундуков",
				["tidychests_found"] = "{0}: есть в сундуках ({1})",
				["tidychests_not_found"] = "{0}: нет ни в одном сундуке в радиусе {1} м",
				["tidychests_hover"] = "Наведите курсор на предмет в инвентаре или в крафте и нажмите {0}, чтобы найти его в сундуках"
			}
		};

		public static string Get(string key, params object[] args)
		{
			string text = Localization.instance.Localize("$" + key);
			if (args.Length != 0)
			{
				return string.Format(text, args);
			}
			return text;
		}

		public static void ApplyIfLoaded()
		{
			Localization instance = Localization.m_instance;
			if (instance != null)
			{
				Apply(instance, instance.GetSelectedLanguage());
			}
		}

		public static void Apply(Localization localization, string language)
		{
			Dictionary<string, string> dictionary = Words["English"];
			Words.TryGetValue(language ?? "", out Dictionary<string, string> value);
			foreach (KeyValuePair<string, string> item in dictionary)
			{
				string value2;
				string text = ((value != null && value.TryGetValue(item.Key, out value2)) ? value2 : item.Value);
				localization.AddWord(item.Key, text);
			}
		}
	}
	[HarmonyPatch(typeof(Localization), "SetupLanguage")]
	internal static class Localization_SetupLanguage_Patch
	{
		private static void Postfix(Localization __instance, string language)
		{
			Translations.Apply(__instance, language);
			StashButton.RefreshLabel();
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "muindor.TidyChests";

		public const string PLUGIN_NAME = "Tidy Chests";

		public const string PLUGIN_VERSION = "1.0.1";
	}
}
namespace TidyChests.Ui
{
	internal static class StashButton
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__OnClick;
		}

		private const string ButtonName = "TidyChests Stash";

		private static Button? _button;

		private static TMP_Text? _label;

		private static bool _warned;

		public static void Ensure(InventoryGui gui)
		{
			if (!Plugin.Enabled || !Plugin.Settings.ShowButton.Value)
			{
				if ((Object)(object)_button != (Object)null)
				{
					((Component)_button).gameObject.SetActive(false);
				}
				return;
			}
			if ((Object)(object)_button != (Object)null)
			{
				((Component)_button).gameObject.SetActive(true);
				return;
			}
			try
			{
				Create(gui);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Could not add the Stash button to the inventory: {arg}");
			}
		}

		public static void RefreshLabel()
		{
			if ((Object)(object)_label != (Object)null)
			{
				_label.text = Translations.Get("tidychests_button");
			}
		}

		private static void Create(InventoryGui gui)
		{
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Expected O, but got Unknown
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: 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_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Expected O, but got Unknown
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: 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_01f7: 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_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: 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_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_029d: 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)
			Button takeAllButton = gui.m_takeAllButton;
			RectTransform player = gui.m_player;
			if ((Object)(object)takeAllButton == (Object)null || (Object)(object)player == (Object)null)
			{
				if (!_warned)
				{
					_warned = true;
					Plugin.Log.LogWarning((object)"The inventory has no 'take all' button to copy (another UI mod?); the Stash button is not shown. Use the console command 'tidychests stash'.");
				}
				return;
			}
			UIGamePad component = ((Component)takeAllButton).GetComponent<UIGamePad>();
			bool enabled = (Object)(object)component != (Object)null && ((Behaviour)component).enabled;
			if ((Object)(object)component != (Object)null)
			{
				((Behaviour)component).enabled = false;
			}
			Button val;
			try
			{
				val = Object.Instantiate<Button>(takeAllButton, (Transform)(object)player);
			}
			finally
			{
				if ((Object)(object)component != (Object)null)
				{
					((Behaviour)component).enabled = enabled;
				}
			}
			((Object)val).name = "TidyChests Stash";
			UIGamePad component2 = ((Component)val).GetComponent<UIGamePad>();
			if ((Object)(object)component2 != (Object)null)
			{
				if ((Object)(object)component2.m_hint != (Object)null)
				{
					component2.m_hint.SetActive(false);
				}
				Object.Destroy((Object)(object)component2);
			}
			((UnityEventBase)val.onClick).RemoveAllListeners();
			ButtonClickedEvent onClick = val.onClick;
			object obj = <>O.<0>__OnClick;
			if (obj == null)
			{
				UnityAction val2 = OnClick;
				<>O.<0>__OnClick = val2;
				obj = (object)val2;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj);
			RectTransform val3 = (RectTransform)((Component)val).transform;
			Vector2 value = Plugin.Settings.ButtonSize.Value;
			val3.SetSizeWithCurrentAnchors((Axis)0, Mathf.Max(20f, value.x));
			val3.SetSizeWithCurrentAnchors((Axis)1, Mathf.Max(16f, value.y));
			Vector2 value2 = Plugin.Settings.ButtonOffset.Value;
			Transform val4 = ((Transform)player).Find("Weight");
			if ((Object)(object)val4 != (Object)null)
			{
				((Component)val).transform.localPosition = val4.localPosition + new Vector3(value2.x, value2.y, 0f);
			}
			else
			{
				val3.anchorMin = new Vector2(1f, 1f);
				val3.anchorMax = new Vector2(1f, 1f);
				val3.pivot = new Vector2(1f, 1f);
				val3.anchoredPosition = new Vector2(-16f, -16f) + value2;
				Plugin.Debug("Weight display not found in the inventory panel; the Stash button is anchored to the top-right corner");
			}
			_label = ((Component)val).GetComponentInChildren<TMP_Text>(true);
			if ((Object)(object)_label != (Object)null)
			{
				_label.enableAutoSizing = true;
				_label.fontSizeMax = _label.fontSize;
				_label.fontSizeMin = Mathf.Min(10f, _label.fontSize);
			}
			_button = val;
			RefreshLabel();
			object arg = ((Component)val).transform.localPosition;
			Rect rect = val3.rect;
			object arg2 = ((Rect)(ref rect)).width;
			rect = val3.rect;
			Plugin.Debug($"Stash button created at {arg} ({arg2}x{((Rect)(ref rect)).height})");
		}

		private static void OnClick()
		{
			try
			{
				Stasher.Stash(Player.m_localPlayer);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Stash failed: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Show")]
	internal static class InventoryGui_Show_Patch
	{
		private static void Postfix(InventoryGui __instance)
		{
			StashButton.Ensure(__instance);
		}
	}
}
namespace TidyChests.Stash
{
	public readonly struct ItemFacts
	{
		public string PrefabName { get; }

		public string SharedName { get; }

		public string TypeName { get; }

		public int MaxStack { get; }

		public bool Equipped { get; }

		public bool Hotbar { get; }

		public bool ModSlot { get; }

		public bool QuestItem { get; }

		public string DisplayName
		{
			get
			{
				if (PrefabName.Length <= 0)
				{
					return SharedName;
				}
				return PrefabName;
			}
		}

		public ItemFacts(string prefabName, string sharedName, string typeName, int maxStack, bool equipped, bool hotbar, bool modSlot, bool questItem)
		{
			PrefabName = prefabName ?? "";
			SharedName = sharedName ?? "";
			TypeName = typeName ?? "";
			MaxStack = maxStack;
			Equipped = equipped;
			Hotbar = hotbar;
			ModSlot = modSlot;
			QuestItem = questItem;
		}
	}
	internal static class Stasher
	{
		public static string Stash(Player player)
		{
			//IL_0335: Unknown result type (might be due to invalid IL or missing references)
			//IL_033a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0348: Unknown result type (might be due to invalid IL or missing references)
			//IL_034e: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.Enabled)
			{
				return "disabled";
			}
			if ((Object)(object)player == (Object)null || ((Character)player).IsTeleporting())
			{
				return "no player";
			}
			ModConfig settings = Plugin.Settings;
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.SetupDragItem((ItemData)null, (Inventory)null, 1);
			}
			List<ItemSnapshot> snapshots;
			List<ItemData> list = Candidates(player, settings, out snapshots);
			if (list.Count == 0)
			{
				return Report(player, settings, Translations.Get("tidychests_nothing"), "no stashable items");
			}
			bool multiUserChestLoaded = ChestMods.MultiUserChestLoaded;
			long playerID = Game.instance.GetPlayerProfile().GetPlayerID();
			List<ContainerSnapshot> snapshots2;
			List<Container> list2 = UsableContainers(player, settings, playerID, multiUserChestLoaded, out snapshots2);
			if (list2.Count == 0)
			{
				string text = settings.Radius.Value.ToString("0.#", CultureInfo.InvariantCulture);
				return Report(player, settings, Translations.Get("tidychests_no_chests", text), "no usable chest in range");
			}
			StashPlan stashPlan = StashPlanner.Plan(snapshots, snapshots2);
			Plugin.Debug($"Plan: {stashPlan.Moves.Count} moves, {stashPlan.Units} units from {stashPlan.ItemsTouched} stacks into {stashPlan.ContainersUsed} chests");
			if (stashPlan.Moves.Count == 0)
			{
				return Report(player, settings, Translations.Get("tidychests_nothing"), "no chest holds these items");
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			int num = 0;
			HashSet<Container> hashSet = new HashSet<Container>();
			foreach (KeyValuePair<int, List<StashMove>> item in GroupByContainer(stashPlan.Moves))
			{
				Container val = list2[item.Key];
				if ((Object)(object)val == (Object)null || val.GetInventory() == null)
				{
					continue;
				}
				if (!ContainerAccess.PrepareForWrite(val, multiUserChestLoaded))
				{
					Plugin.Log.LogWarning((object)("Could not take ownership of " + ContainerAccess.NameOf(val) + ", skipping it"));
					continue;
				}
				foreach (StashMove item2 in item.Value)
				{
					ItemData val2 = list[item2.Item];
					int num2 = MoveInto(inventory, val2, val.GetInventory(), item2.Amount);
					Plugin.Debug(string.Format("  {0} x{1} -> {2} ({3} m): {4} moved", val2.m_shared.m_name, item2.Amount, ContainerAccess.NameOf(val), snapshots2[item.Key].Distance.ToString("0.#", CultureInfo.InvariantCulture), num2));
					if (num2 > 0)
					{
						num += num2;
						hashSet.Add(val);
					}
				}
			}
			inventory.Changed(false, false);
			foreach (Container item3 in hashSet)
			{
				if ((Object)(object)instance != (Object)null && (Object)(object)item3 != (Object)null)
				{
					instance.m_moveItemEffects.Create(((Component)item3).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
				}
			}
			string message = ((num > 0) ? Translations.Get("tidychests_stashed", num, hashSet.Count) : Translations.Get("tidychests_nothing"));
			return Report(player, settings, message, $"{num} units into {hashSet.Count} chests");
		}

		private static List<ItemData> Candidates(Player player, ModConfig settings, out List<ItemSnapshot> snapshots)
		{
			StashRules stashRules = settings.BuildRules();
			Func<ItemData, bool> classifier = SlotMods.GetClassifier();
			Inventory inventory = ((Humanoid)player).GetInventory();
			List<ItemData> list = new List<ItemData>(inventory.GetAllItems());
			list.Sort((ItemData a, ItemData b) => (a.m_gridPos.y == b.m_gridPos.y) ? a.m_gridPos.x.CompareTo(b.m_gridPos.x) : a.m_gridPos.y.CompareTo(b.m_gridPos.y));
			Plugin.Debug($"Stash by {player.GetPlayerName()}: {list.Count} items, inventory {inventory.GetWidth()}x{inventory.GetHeight()}, {stashRules.Describe()}");
			List<ItemData> list2 = new List<ItemData>();
			snapshots = new List<ItemSnapshot>();
			foreach (ItemData item in list)
			{
				ItemFacts facts = Describe(item, player, classifier);
				StashVerdict stashVerdict = stashRules.Judge(facts);
				Plugin.Debug($"  {facts.DisplayName} x{item.m_stack} at [{item.m_gridPos.x},{item.m_gridPos.y}] ({facts.TypeName}) -> {stashVerdict}");
				if (stashVerdict == StashVerdict.Stash)
				{
					snapshots.Add(new ItemSnapshot(list2.Count, item.m_shared.m_name, item.m_quality, item.m_worldLevel, item.m_stack, item.m_shared.m_maxStackSize));
					list2.Add(item);
				}
			}
			return list2;
		}

		private static List<Container> UsableContainers(Player player, ModConfig settings, long playerId, bool multiUserChest, out List<ContainerSnapshot> snapshots)
		{
			//IL_000c: 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_00b9: Unknown result type (might be due to invalid IL or missing references)
			List<Container> list = new List<Container>();
			ContainerRegistry.CollectNearby(((Component)player).transform.position, settings.Radius.Value, list);
			Plugin.Debug(string.Format("{0} containers within {1} m ({2} loaded), MultiUserChest {3}", list.Count, settings.Radius.Value.ToString("0.#", CultureInfo.InvariantCulture), ContainerRegistry.Count, multiUserChest ? "on" : "off"));
			List<Container> list2 = new List<Container>();
			snapshots = new List<ContainerSnapshot>();
			foreach (Container item in list)
			{
				float distance = Vector3.Distance(((Component)item).transform.position, ((Component)player).transform.position);
				if (!ContainerAccess.CanStashInto(item, playerId, multiUserChest, out string reason))
				{
					Plugin.Debug("  skip " + ContainerAccess.NameOf(item) + " at " + distance.ToString("0.#", CultureInfo.InvariantCulture) + " m: " + reason);
					continue;
				}
				Inventory inventory = item.GetInventory();
				List<StackSnapshot> list3 = new List<StackSnapshot>();
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					list3.Add(new StackSnapshot(allItem.m_shared.m_name, allItem.m_quality, allItem.m_worldLevel, allItem.m_stack, allItem.m_shared.m_maxStackSize));
				}
				snapshots.Add(new ContainerSnapshot(list2.Count, ContainerAccess.NameOf(item), distance, list3, inventory.GetEmptySlots()));
				list2.Add(item);
			}
			return list2;
		}

		private static int MoveInto(Inventory from, ItemData item, Inventory target, int amount)
		{
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			if (!from.ContainsItem(item))
			{
				return 0;
			}
			amount = Math.Min(amount, item.m_stack);
			if (amount <= 0)
			{
				return 0;
			}
			if (amount == item.m_stack)
			{
				if (target.AddItem(item))
				{
					from.RemoveItem(item);
					return amount;
				}
				if (!from.ContainsItem(item))
				{
					return amount;
				}
				return Math.Max(0, amount - item.m_stack);
			}
			int num = 0;
			foreach (ItemData item2 in new List<ItemData>(target.GetAllItems()))
			{
				if (num >= amount || !from.ContainsItem(item))
				{
					break;
				}
				if (!(item2.m_shared.m_name != item.m_shared.m_name) && item2.m_quality == item.m_quality && item2.m_worldLevel == item.m_worldLevel)
				{
					int num2 = item2.m_shared.m_maxStackSize - item2.m_stack;
					if (num2 > 0)
					{
						num += MoveTo(from, item, target, Math.Min(num2, amount - num), item2.m_gridPos);
					}
				}
			}
			for (int i = 0; i < target.GetHeight(); i++)
			{
				if (num >= amount)
				{
					break;
				}
				for (int j = 0; j < target.GetWidth(); j++)
				{
					if (num >= amount)
					{
						break;
					}
					if (from.ContainsItem(item) && target.GetItemAt(j, i) == null)
					{
						num += MoveTo(from, item, target, Math.Min(item.m_shared.m_maxStackSize, amount - num), new Vector2i(j, i));
					}
				}
			}
			return num;
		}

		private static int MoveTo(Inventory from, ItemData item, Inventory target, int units, Vector2i cell)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			int stack = item.m_stack;
			if (target.MoveItemToThis(from, item, units, cell.x, cell.y))
			{
				return units;
			}
			if (!from.ContainsItem(item))
			{
				return stack;
			}
			return Math.Max(0, stack - item.m_stack);
		}

		private static IEnumerable<KeyValuePair<int, List<StashMove>>> GroupByContainer(IReadOnlyList<StashMove> moves)
		{
			List<int> list = new List<int>();
			Dictionary<int, List<StashMove>> groups = new Dictionary<int, List<StashMove>>();
			foreach (StashMove move in moves)
			{
				if (!groups.TryGetValue(move.Container, out List<StashMove> value))
				{
					value = new List<StashMove>();
					groups[move.Container] = value;
					list.Add(move.Container);
				}
				value.Add(move);
			}
			foreach (int item in list)
			{
				yield return new KeyValuePair<int, List<StashMove>>(item, groups[item]);
			}
		}

		private static ItemFacts Describe(ItemData item, Player player, Func<ItemData, bool>? inModSlot)
		{
			SharedData shared = item.m_shared;
			return new ItemFacts(((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : "", shared.m_name, ((object)Unsafe.As<ItemType, ItemType>(ref shared.m_itemType)/*cast due to .constrained prefix*/).ToString(), shared.m_maxStackSize, item.m_equipped || ((Humanoid)player).IsItemEquiped(item), item.m_gridPos.y == 0, inModSlot?.Invoke(item) ?? false, shared.m_questItem);
		}

		private static string Report(Player player, ModConfig settings, string message, string summary)
		{
			Plugin.Debug("Stash result: " + summary);
			if (settings.ShowMessage.Value)
			{
				((Character)player).Message((MessageType)2, message, 0, (Sprite)null, false);
			}
			return summary;
		}
	}
	public sealed class ItemSnapshot
	{
		public int Index { get; }

		public string Name { get; }

		public int Quality { get; }

		public int WorldLevel { get; }

		public int Count { get; }

		public int MaxStack { get; }

		public ItemSnapshot(int index, string name, int quality, int worldLevel, int count, int maxStack)
		{
			Index = index;
			Name = name;
			Quality = quality;
			WorldLevel = worldLevel;
			Count = count;
			MaxStack = maxStack;
		}
	}
	public sealed class StackSnapshot
	{
		public string Name { get; }

		public int Quality { get; }

		public int WorldLevel { get; }

		public int Count { get; set; }

		public int MaxStack { get; }

		public int FreeSpace => MaxStack - Count;

		public StackSnapshot(string name, int quality, int worldLevel, int count, int maxStack)
		{
			Name = name;
			Quality = quality;
			WorldLevel = worldLevel;
			Count = count;
			MaxStack = maxStack;
		}

		public bool Accepts(ItemSnapshot item)
		{
			if (MaxStack > 1 && Name == item.Name && Quality == item.Quality)
			{
				return WorldLevel == item.WorldLevel;
			}
			return false;
		}
	}
	public sealed class ContainerSnapshot
	{
		public int Index { get; }

		public string Name { get; }

		public float Distance { get; }

		public List<StackSnapshot> Stacks { get; }

		public int EmptySlots { get; set; }

		public ContainerSnapshot(int index, string name, float distance, List<StackSnapshot> stacks, int emptySlots)
		{
			Index = index;
			Name = name;
			Distance = distance;
			Stacks = stacks;
			EmptySlots = emptySlots;
		}

		public bool Holds(string name)
		{
			foreach (StackSnapshot stack in Stacks)
			{
				if (stack.Name == name)
				{
					return true;
				}
			}
			return false;
		}
	}
	public readonly struct StashMove
	{
		public int Item { get; }

		public int Container { get; }

		public int Amount { get; }

		public StashMove(int item, int container, int amount)
		{
			Item = item;
			Container = container;
			Amount = amount;
		}
	}
	public sealed class StashPlan
	{
		public IReadOnlyList<StashMove> Moves { get; }

		public int Units { get; }

		public int ItemsTouched { get; }

		public int ContainersUsed { get; }

		public StashPlan(IReadOnlyList<StashMove> moves, int units, int itemsTouched, int containersUsed)
		{
			Moves = moves;
			Units = units;
			ItemsTouched = itemsTouched;
			ContainersUsed = containersUsed;
		}
	}
	public static class StashPlanner
	{
		public static StashPlan Plan(IReadOnlyList<ItemSnapshot> items, IReadOnlyList<ContainerSnapshot> containers)
		{
			List<ContainerSnapshot> list = containers.OrderBy((ContainerSnapshot container) => container.Distance).ToList();
			List<StashMove> list2 = new List<StashMove>();
			HashSet<int> hashSet = new HashSet<int>();
			HashSet<int> hashSet2 = new HashSet<int>();
			int num = 0;
			foreach (ItemSnapshot item in items)
			{
				int num2 = item.Count;
				foreach (ContainerSnapshot item2 in list)
				{
					if (num2 <= 0)
					{
						break;
					}
					if (item2.Holds(item.Name))
					{
						int num3 = Fill(item2, item, num2);
						if (num3 > 0)
						{
							num2 -= num3;
							num += num3;
							hashSet.Add(item.Index);
							hashSet2.Add(item2.Index);
							list2.Add(new StashMove(item.Index, item2.Index, num3));
						}
					}
				}
			}
			return new StashPlan(list2, num, hashSet.Count, hashSet2.Count);
		}

		private static int Fill(ContainerSnapshot container, ItemSnapshot item, int remaining)
		{
			int num = 0;
			foreach (StackSnapshot stack in container.Stacks)
			{
				if (remaining <= 0)
				{
					break;
				}
				if (stack.Accepts(item) && stack.FreeSpace > 0)
				{
					int num2 = Math.Min(stack.FreeSpace, remaining);
					stack.Count += num2;
					remaining -= num2;
					num += num2;
				}
			}
			while (remaining > 0 && container.EmptySlots > 0)
			{
				int num3 = Math.Min(Math.Max(1, item.MaxStack), remaining);
				container.Stacks.Add(new StackSnapshot(item.Name, item.Quality, item.WorldLevel, num3, item.MaxStack));
				container.EmptySlots--;
				remaining -= num3;
				num += num3;
			}
			return num;
		}
	}
	public sealed class StashRules
	{
		private readonly bool _includeHotbar;

		private readonly HashSet<string> _types;

		private readonly HashSet<string> _blacklist;

		public IReadOnlyList<string> ItemTypes => _types.OrderBy<string, string>((string type) => type, StringComparer.OrdinalIgnoreCase).ToList();

		public IReadOnlyList<string> Blacklist => _blacklist.OrderBy<string, string>((string name) => name, StringComparer.OrdinalIgnoreCase).ToList();

		private StashRules(bool includeHotbar, HashSet<string> types, HashSet<string> blacklist)
		{
			_includeHotbar = includeHotbar;
			_types = types;
			_blacklist = blacklist;
		}

		public static StashRules Parse(StashRuleSettings settings, IEnumerable<string> knownTypeNames, Action<string>? warn = null)
		{
			HashSet<string> hashSet = new HashSet<string>(knownTypeNames, StringComparer.OrdinalIgnoreCase);
			HashSet<string> hashSet2 = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (string item in Split(settings.ItemTypes))
			{
				if (hashSet.Contains(item))
				{
					hashSet2.Add(item);
				}
				else
				{
					warn?.Invoke("Unknown item type '" + item + "' in ItemTypes is ignored. Valid types: " + string.Join(", ", knownTypeNames));
				}
			}
			HashSet<string> hashSet3 = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (string item2 in Split(settings.Blacklist))
			{
				hashSet3.Add(Normalize(item2));
			}
			return new StashRules(settings.IncludeHotbar, hashSet2, hashSet3);
		}

		public StashVerdict Judge(ItemFacts facts)
		{
			if (facts.MaxStack <= 1)
			{
				return StashVerdict.NotStackable;
			}
			if (facts.QuestItem)
			{
				return StashVerdict.QuestItem;
			}
			if (facts.Equipped)
			{
				return StashVerdict.Equipped;
			}
			if (facts.ModSlot)
			{
				return StashVerdict.ModSlot;
			}
			if (facts.Hotbar && !_includeHotbar)
			{
				return StashVerdict.Hotbar;
			}
			if (!_types.Contains(facts.TypeName))
			{
				return StashVerdict.TypeExcluded;
			}
			if (IsBlacklisted(facts.PrefabName) || IsBlacklisted(facts.SharedName))
			{
				return StashVerdict.Blacklisted;
			}
			return StashVerdict.Stash;
		}

		public string Describe()
		{
			string text = ((_types.Count == 0) ? "none" : string.Join(", ", ItemTypes));
			string text2 = ((_blacklist.Count == 0) ? "empty" : string.Join(", ", Blacklist));
			return "types: " + text + "; blacklist: " + text2;
		}

		private bool IsBlacklisted(string name)
		{
			string text = Normalize(name);
			if (text.Length > 0)
			{
				return _blacklist.Contains(text);
			}
			return false;
		}

		private static IEnumerable<string> Split(string list)
		{
			if (string.IsNullOrWhiteSpace(list))
			{
				yield break;
			}
			string[] array = list.Split(',', ';');
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					yield return text;
				}
			}
		}

		private static string Normalize(string name)
		{
			return (name ?? "").Trim().TrimStart(new char[1] { '$' });
		}
	}
	public sealed class StashRuleSettings
	{
		public bool IncludeHotbar { get; set; }

		public string ItemTypes { get; set; } = "";

		public string Blacklist { get; set; } = "";
	}
	public enum StashVerdict
	{
		Stash,
		NotStackable,
		QuestItem,
		Equipped,
		ModSlot,
		Hotbar,
		TypeExcluded,
		Blacklisted
	}
}
namespace TidyChests.Patches
{
	[HarmonyPatch(typeof(Terminal), "InitTerminal")]
	internal static class Terminal_InitTerminal_Patch
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__Execute;
		}

		private const string Command = "tidychests";

		private static void Postfix()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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_0025: Expected O, but got Unknown
			object obj = <>O.<0>__Execute;
			if (obj == null)
			{
				ConsoleEvent val = Execute;
				<>O.<0>__Execute = val;
				obj = (object)val;
			}
			new ConsoleCommand("tidychests", "tidychests [stash|reload|status] - stash items into nearby chests, reload the Tidy Chests config or show the active settings", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}

		private static void Execute(ConsoleEventArgs args)
		{
			Terminal context = args.Context;
			switch ((args.Args.Length > 1) ? args.Args[1].ToLowerInvariant() : "status")
			{
			case "stash":
				if ((Object)(object)Player.m_localPlayer == (Object)null)
				{
					context.AddString("Tidy Chests: no player in the world");
				}
				else
				{
					context.AddString("Tidy Chests: " + Stasher.Stash(Player.m_localPlayer));
				}
				break;
			case "reload":
				Plugin.Settings.Reload();
				context.AddString("Tidy Chests: config reloaded, " + Plugin.Settings.Describe());
				break;
			case "status":
				context.AddString(string.Format("{0} {1}: {2}; {3} chests highlighted", "Tidy Chests", "1.0.1", Plugin.Settings.Describe(), Plugin.Finder?.HighlightCount ?? 0));
				break;
			default:
				context.AddString("Usage: tidychests [stash|reload|status]");
				break;
			}
		}
	}
}
namespace TidyChests.Hud
{
	internal static class MarkerOverlay
	{
		private const int ArrowTextureSize = 48;

		private const float ArrowSize = 32f;

		private const float EdgeMargin = 56f;

		private static Texture2D? _arrow;

		private static GUIStyle? _label;

		public static void Draw(IReadOnlyList<ChestHighlight> highlights, Camera camera, Vector3 playerPosition)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: 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_0089: 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_00ae: 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_00be: 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_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Expected O, but got Unknown
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: 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_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: 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_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: 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_011b: 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_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			if ((int)Event.current.type != 7)
			{
				return;
			}
			Texture2D arrow = _arrow ?? (_arrow = CreateArrow(48));
			GUIStyle val = _label ?? (_label = CreateLabelStyle());
			float num = Screen.width;
			float num2 = Screen.height;
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(num / 2f, num2 / 2f);
			Vector2 val6 = default(Vector2);
			Vector2 val7 = default(Vector2);
			foreach (ChestHighlight highlight in highlights)
			{
				Vector3 val3 = camera.WorldToViewportPoint(highlight.Position);
				ScreenMarker screenMarker = ScreenMarkerMath.Compute(val3.x, val3.y, val3.z, num, num2, 56f);
				float alpha = highlight.Alpha;
				Color color = ChestHighlight.Color;
				color.a = alpha;
				float num3 = Vector3.Distance(playerPosition, highlight.Position);
				GUIContent val4 = new GUIContent($"{highlight.Label}  {num3:0} m");
				Vector2 val5 = val.CalcSize(val4);
				if (screenMarker.OnScreen)
				{
					((Vector2)(ref val6))..ctor(screenMarker.X, screenMarker.Y - 10f);
					DrawArrow(arrow, val6, screenMarker.AngleDegrees, color);
					DrawLabel(val, val4, val5, new Vector2(val6.x, val6.y - 32f - 8f - val5.y / 2f), alpha);
					continue;
				}
				((Vector2)(ref val7))..ctor(screenMarker.X, screenMarker.Y);
				DrawArrow(arrow, val7, screenMarker.AngleDegrees, color);
				Vector2 val8 = val2 - val7;
				Vector2 normalized = ((Vector2)(ref val8)).normalized;
				float num4 = 40f + Mathf.Abs(normalized.x) * val5.x / 2f + Mathf.Abs(normalized.y) * val5.y / 2f;
				DrawLabel(val, val4, val5, val7 + normalized * num4, alpha);
			}
		}

		private static void DrawArrow(Texture2D arrow, Vector2 tip, float angle, Color color)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: 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)
			Matrix4x4 matrix = GUI.matrix;
			Color color2 = GUI.color;
			GUIUtility.RotateAroundPivot(angle, tip);
			float num = 40f;
			GUI.color = new Color(0f, 0f, 0f, 0.7f * color.a);
			GUI.DrawTexture(new Rect(tip.x - num / 2f, tip.y - (num - 32f) / 2f, num, num), (Texture)(object)arrow);
			GUI.color = color;
			GUI.DrawTexture(new Rect(tip.x - 16f, tip.y, 32f, 32f), (Texture)(object)arrow);
			GUI.color = color2;
			GUI.matrix = matrix;
		}

		private static void DrawLabel(GUIStyle style, GUIContent content, Vector2 size, Vector2 center, float alpha)
		{
			//IL_0002: 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_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: 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_0081: 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_00a8: Unknown result type (might be due to invalid IL or missing references)
			Rect val = default(Rect);
			((Rect)(ref val))..ctor(center.x - size.x / 2f, center.y - size.y / 2f, size.x, size.y);
			Color color = GUI.color;
			GUI.color = new Color(0f, 0f, 0f, alpha);
			GUI.Label(new Rect(((Rect)(ref val)).x + 1f, ((Rect)(ref val)).y + 1f, ((Rect)(ref val)).width, ((Rect)(ref val)).height), content, style);
			GUI.color = new Color(1f, 1f, 1f, alpha);
			GUI.Label(val, content, style);
			GUI.color = color;
		}

		private static GUIStyle CreateLabelStyle()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: 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_001e: 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_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_003d: Expected O, but got Unknown
			GUIStyle val = new GUIStyle(GUI.skin.label)
			{
				fontSize = 16,
				fontStyle = (FontStyle)1,
				alignment = (TextAnchor)4,
				wordWrap = false
			};
			val.normal.textColor = Color.white;
			return val;
		}

		private static Texture2D CreateArrow(int size)
		{
			//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_0018: 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_002b: Expected O, but got Unknown
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(size, size, (TextureFormat)4, false)
			{
				hideFlags = (HideFlags)61,
				wrapMode = (TextureWrapMode)1,
				filterMode = (FilterMode)1,
				name = "TidyChests arrow"
			};
			float num = (float)size / 2f;
			float num2 = (float)size - 1f;
			float num3 = 1f;
			float num4 = (float)size / 2f - 1f;
			Color32[] array = (Color32[])(object)new Color32[size * size];
			for (int i = 0; i < size; i++)
			{
				for (int j = 0; j < size; j++)
				{
					int num5 = 0;
					for (int k = 0; k < 2; k++)
					{
						for (int l = 0; l < 2; l++)
						{
							float num6 = (float)j + 0.25f + (float)l * 0.5f;
							float num7 = (float)i + 0.25f + (float)k * 0.5f;
							float num8 = (num7 - num3) / (num2 - num3);
							float num9 = num4 * (1f - num8);
							if (num7 >= num3 && num7 <= num2 && Mathf.Abs(num6 - num) <= num9)
							{
								num5++;
							}
						}
					}
					byte b = (byte)(num5 * 255 / 4);
					array[i * size + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, b);
				}
			}
			val.SetPixels32(array);
			val.Apply(false, true);
			return val;
		}
	}
	public readonly struct ScreenMarker
	{
		public float X { get; }

		public float Y { get; }

		public float AngleDegrees { get; }

		public bool OnScreen { get; }

		public ScreenMarker(float x, float y, float angleDegrees, bool onScreen)
		{
			X = x;
			Y = y;
			AngleDegrees = angleDegrees;
			OnScreen = onScreen;
		}
	}
	public static class ScreenMarkerMath
	{
		private const float Epsilon = 0.0001f;

		public static ScreenMarker Compute(float vx, float vy, float vz, float width, float height, float margin)
		{
			bool flag = !IsBad(vx) && !IsBad(vy) && !IsBad(vz);
			bool flag2 = vz > 0f;
			if (flag && flag2 && vx >= 0f && vx <= 1f && vy >= 0f && vy <= 1f)
			{
				return new ScreenMarker(vx * width, (1f - vy) * height, 180f, onScreen: true);
			}
			float num = width / 2f;
			float num2 = height / 2f;
			float num3 = 0f;
			float num4 = 1f;
			if (flag)
			{
				num3 = vx * width - num;
				num4 = (1f - vy) * height - num2;
				if (!flag2)
				{
					num3 = 0f - num3;
					num4 = 0f - num4;
				}
				if (Math.Abs(num3) < 0.0001f && Math.Abs(num4) < 0.0001f)
				{
					num3 = 0f;
					num4 = 1f;
				}
			}
			float num5 = Math.Max(1f, num - margin);
			float num6 = Math.Max(1f, num2 - margin);
			float num7 = float.MaxValue;
			if (Math.Abs(num3) > 0.0001f)
			{
				num7 = Math.Min(num7, num5 / Math.Abs(num3));
			}
			if (Math.Abs(num4) > 0.0001f)
			{
				num7 = Math.Min(num7, num6 / Math.Abs(num4));
			}
			float angleDegrees = (float)(Math.Atan2(num3, 0f - num4) * 180.0 / Math.PI);
			return new ScreenMarker(num + num3 * num7, num2 + num4 * num7, angleDegrees, onScreen: false);
		}

		private static bool IsBad(float value)
		{
			if (!float.IsNaN(value))
			{
				return float.IsInfinity(value);
			}
			return true;
		}
	}
}
namespace TidyChests.Find
{
	public sealed class ChestFinder : MonoBehaviour
	{
		private readonly List<ChestHighlight> _highlights = new List<ChestHighlight>();

		private readonly List<Container> _nearby = new List<Container>();

		private bool _overlayFailed;

		public int HighlightCount => _highlights.Count;

		public int Find(string sharedName, string displayName)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			ModConfig settings = Plugin.Settings;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return 0;
			}
			RemoveHighlights();
			_nearby.Clear();
			ContainerRegistry.CollectNearby(((Component)localPlayer).transform.position, settings.Radius.Value, _nearby);
			HighlightOptions options = settings.ToHighlightOptions();
			foreach (Container item in _nearby)
			{
				Inventory inventory = item.GetInventory();
				if (inventory == null || IsCarriedByAnotherPlayer(item))
				{
					continue;
				}
				int num = inventory.CountItems(sharedName, -1, false);
				if (num > 0)
				{
					try
					{
						_highlights.Add(new ChestHighlight(item, $"{num}x {displayName}", options));
						Plugin.Debug(string.Format("  {0} at {1} m holds {2}", ContainerAccess.NameOf(item), Vector3.Distance(((Component)item).transform.position, ((Component)localPlayer).transform.position).ToString("0.#", CultureInfo.InvariantCulture), num));
					}
					catch (Exception ex)
					{
						Plugin.Log.LogWarning((object)("Could not highlight " + ContainerAccess.NameOf(item) + ": " + ex.Message));
					}
				}
			}
			return _highlights.Count;
		}

		public void RemoveHighlights()
		{
			foreach (ChestHighlight highlight in _highlights)
			{
				highlight.Remove();
			}
			_highlights.Clear();
		}

		private void Update()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!Plugin.Enabled || (Object)(object)Player.m_localPlayer == (Object)null)
				{
					if (_highlights.Count > 0)
					{
						RemoveHighlights();
					}
					return;
				}
				UpdateHighlights();
				if (InventoryGui.IsVisible() && IsPressed(Plugin.Settings.FindKey.Value))
				{
					FindHovered();
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Update failed: {arg}");
			}
		}

		private void OnGUI()
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Invalid comparison between Unknown and I4
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			if (_overlayFailed || _highlights.Count == 0)
			{
				return;
			}
			ModConfig settings = Plugin.Settings;
			if (!settings.Enabled.Value || !settings.ScreenMarker.Value || (int)Event.current.type != 7)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || Hud.IsUserHidden() || Minimap.IsOpen() || Menu.IsVisible())
			{
				return;
			}
			Camera mainCamera = Utils.GetMainCamera();
			if ((Object)(object)mainCamera == (Object)null)
			{
				return;
			}
			try
			{
				MarkerOverlay.Draw(_highlights, mainCamera, ((Component)localPlayer).transform.position);
			}
			catch (Exception arg)
			{
				_overlayFailed = true;
				Plugin.Log.LogError((object)$"Screen markers disabled until restart: {arg}");
			}
		}

		private void OnDestroy()
		{
			RemoveHighlights();
		}

		private void UpdateHighlights()
		{
			for (int num = _highlights.Count - 1; num >= 0; num--)
			{
				ChestHighlight chestHighlight = _highlights[num];
				if (chestHighlight.Expired)
				{
					chestHighlight.Remove();
					_highlights.RemoveAt(num);
				}
				else
				{
					chestHighlight.Update();
				}
			}
		}

		private void FindHovered()
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			InventoryGui instance = InventoryGui.instance;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			string text = HoveredInventoryItem(instance)?.m_shared.m_name ?? HoveredCraftingItem(instance);
			if (text == null)
			{
				((Character)localPlayer).Message((MessageType)1, Translations.Get("tidychests_hover", Plugin.Settings.FindKey.Value), 0, (Sprite)null, false);
				return;
			}
			string text2 = Localization.instance.Localize(text);
			Plugin.Debug("Find " + text + " within " + Plugin.Settings.Radius.Value.ToString("0.#", CultureInfo.InvariantCulture) + " m");
			int num = Find(text, text2);
			if (num == 0)
			{
				string text3 = Plugin.Settings.Radius.Value.ToString("0.#", CultureInfo.InvariantCulture);
				((Character)localPlayer).Message((MessageType)2, Translations.Get("tidychests_not_found", text2, text3), 0, (Sprite)null, false);
				return;
			}
			if (Plugin.Settings.CloseInventory.Value)
			{
				instance.Hide();
			}
			((Character)localPlayer).Message((MessageType)2, Translations.Get("tidychests_found", text2, num), 0, (Sprite)null, false);
		}

		private static ItemData? HoveredInventoryItem(InventoryGui gui)
		{
			ItemData val = HoveredItem(gui.m_playerGrid) ?? HoveredItem(gui.m_containerGrid);
			if (val == null && ZInput.IsExclusiveGamepadActive())
			{
				val = gui.m_playerGrid.GetGamepadSelectedItem() ?? gui.m_containerGrid.GetGamepadSelectedItem();
			}
			return val;
		}

		private static string? HoveredCraftingItem(InventoryGui gui)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: 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_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: 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_0216: Unknown result type (might be due to invalid IL or missing references)
			Vector3 pointerPosition = ZInput.pointerPosition;
			GameObject[] recipeRequirementList = gui.m_recipeRequirementList;
			foreach (GameObject val in recipeRequirementList)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Transform val2 = val.transform.Find("res_icon");
				if ((Object)(object)val2 == (Object)null || !((Component)val2).gameObject.activeInHierarchy || (!Contains((RectTransform?)/*isinst with value type is only supported in some contexts*/, pointerPosition) && !Contains((RectTransform?)(object)((val2 is RectTransform) ? val2 : null), pointerPosition)))
				{
					continue;
				}
				Image component = ((Component)val2).GetComponent<Image>();
				Sprite val3 = ((component != null) ? component.sprite : null);
				foreach (Requirement req in gui.m_reqList)
				{
					ItemDrop resItem = req.m_resItem;
					if ((Object)(object)resItem != (Object)null && (Object)(object)val3 != (Object)null && (Object)(object)resItem.m_itemData.GetIcon() == (Object)(object)val3)
					{
						return resItem.m_itemData.m_shared.m_name;
					}
				}
			}
			Recipe recipe = ((RecipeDataPair)(ref gui.m_selectedRecipe)).Recipe;
			if ((Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item != (Object)null && (Object)(object)gui.m_recipeIcon != (Object)null && ((Behaviour)gui.m_recipeIcon).enabled && Contains(((Graphic)gui.m_recipeIcon).rectTransform, pointerPosition))
			{
				return recipe.m_item.m_itemData.m_shared.m_name;
			}
			RectTransform val4 = (RectTransform)(((Object)(object)gui.m_recipeListRoot != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null);
			if ((Object)(object)val4 != (Object)null && Contains(val4, pointerPosition))
			{
				foreach (RecipeDataPair availableRecipe in gui.m_availableRecipes)
				{
					RecipeDataPair current = availableRecipe;
					GameObject interfaceElement = ((RecipeDataPair)(ref current)).InterfaceElement;
					if ((Object)(object)interfaceElement != (Object)null && (Object)(object)((RecipeDataPair)(ref current)).Recipe != (Object)null && (Object)(object)((RecipeDataPair)(ref current)).Recipe.m_item != (Object)null && Contains((RectTransform?)/*isinst with value type is only supported in some contexts*/, pointerPosition))
					{
						return ((RecipeDataPair)(ref current)).Recipe.m_item.m_itemData.m_shared.m_name;
					}
				}
			}
			return null;
		}

		private static bool Contains(RectTransform? rect, Vector3 pointer)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rect == (Object)null || !((Component)rect).gameObject.activeInHierarchy)
			{
				return false;
			}
			Vector2 val = Vector2.op_Implicit(((Transform)rect).InverseTransformPoint(pointer));
			Rect rect2 = rect.rect;
			return ((Rect)(ref rect2)).Contains(val);
		}

		private static ItemData? HoveredItem(InventoryGrid? grid)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)grid == (Object)null || !((Component)grid).gameObject.activeInHierarchy || grid.GetInventory() == null)
			{
				return null;
			}
			InventoryElement hoveredElement = grid.GetHoveredElement();
			if ((Object)(object)hoveredElement == (Object)null)
			{
				return null;
			}
			Vector2i elementPos = grid.GetElementPos(hoveredElement);
			return grid.GetInventory().GetItemAt(elementPos.x, elementPos.y);
		}

		private static bool IsCarriedByAnotherPlayer(Container container)
		{
			ZNetView nview = container.m_nview;
			if ((Object)(object)nview == (Object)null)
			{
				return false;
			}
			Player component = ((Component)nview).GetComponent<Player>();
			if ((Object)(object)component != (Object)null)
			{
				return (Object)(object)component != (Object)(object)Player.m_localPlayer;
			}
			return false;
		}

		private static bool IsPressed(KeyboardShortcut shortcut)
		{
			//IL_0002: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0)
			{
				return false;
			}
			if (Console.IsVisible() || TextInput.IsVisible() || ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()))
			{
				return false;
			}
			if (!ZInput.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey, false))
			{
				return false;
			}
			foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers)
			{
				if (!ZInput.GetKey(modifier, false))
				{
					return false;
				}
			}
			return true;
		}
	}
	public sealed class ChestHighlight
	{
		public static readonly Color Color = new Color(1f, 0.82f, 0.3f);

		private const float LightIntensity = 4f;

		private const float FadeFraction = 0.25f;

		private static readonly int EmissionColorId = Shader.PropertyToID("_EmissionColor");

		private readonly Container _container;

		private readonly float _start;

		private readonly float _duration;

		private readonly List<Renderer> _glowing = new List<Renderer>();

		private GameObject? _root;

		private Light? _light;

		public string Label { get; }

		public Vector3 Position { get; }

		public bool Expired
		{
			get
			{
				if (!((Object)(object)_container == (Object)null))
				{
					return Time.time - _start >= _duration;
				}
				return true;
			}
		}

		public float Alpha => Mathf.Clamp01((_duration - (Time.time - _start)) / (_duration * 0.25f));

		public ChestHighlight(Container container, string label, HighlightOptions options)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: 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_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: 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_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Expected O, but got Unknown
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			_container = container;
			Label = label;
			_start = Time.time;
			_duration = Mathf.Max(0.5f, options.Duration);
			bool found;
			Bounds val = BoundsOf(((Component)container).gameObject, out found);
			Position = (found ? ((Bounds)(ref val)).center : (((Component)container).transform.position + Vector3.up * 0.5f));
			float extent = (found ? Mathf.Max(new float[3]
			{
				((Bounds)(ref val)).extents.x,
				((Bounds)(ref val)).extents.y,
				((Bounds)(ref val)).extents.z
			}) : 1f);
			float top = (found ? ((Bounds)(ref val)).max.y : (Position.y + 0.5f));
			_root = new GameObject("TidyChests highlight");
			_root.transform.position = Position;
			try
			{
				Build(options, top, extent, ((Component)container).gameObject);
			}
			catch
			{
				Remove();
				throw;
			}
		}

		private void Build(HighlightOptions options, float top, float extent, GameObject target)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			if (options.Light)
			{
				GameObject val = new GameObject("Light");
				val.transform.SetParent(_root.transform, false);
				val.transform.position = new Vector3(Position.x, top + 0.8f, Position.z);
				_light = val.AddComponent<Light>();
				_light.type = (LightType)2;
				_light.color = Color;
				_light.range = Mathf.Clamp(extent * 3f + 3f, 4f, 12f);
				_light.intensity = 4f;
				_light.shadows = (LightShadows)0;
				_light.renderMode = (LightRenderMode)1;
			}
			if (options.Glow)
			{
				ApplyGlow(target, Color);
			}
			Update();
		}

		public void Update()
		{
			if ((Object)(object)_light != (Object)null)
			{
				_light.intensity = 4f * (0.7f + 0.3f * Mathf.Sin(Time.time * 5f)) * Alpha;
			}
		}

		public void Remove()
		{
			foreach (Renderer item in _glowing)
			{
				if ((Object)(object)item != (Object)null)
				{
					item.SetPropertyBlock((MaterialPropertyBlock)null);
				}
			}
			_glowing.Clear();
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
				_root = null;
			}
			_light = null;
		}

		private void ApplyGlow(GameObject target, Color color)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			//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_0064: Unknown result type (might be due to invalid IL or missing references)
			MaterialPropertyBlock val = new MaterialPropertyBlock();
			Color val2 = color * 0.5f;
			Renderer[] componentsInChildren = target.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val3 in componentsInChildren)
			{
				if (val3 is MeshRenderer || val3 is SkinnedMeshRenderer)
				{
					Material sharedMaterial = val3.sharedMaterial;
					if (!((Object)(object)sharedMaterial == (Object)null) && sharedMaterial.HasProperty(EmissionColorId))
					{
						val3.GetPropertyBlock(val);
						val.SetColor(EmissionColorId, val2);
						val3.SetPropertyBlock(val);
						_glowing.Add(val3);
					}
				}
			}
		}

		private static Bounds BoundsOf(GameObject target, out bool found)
		{
			//IL_0002: 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_0046: 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)
			Bounds result = default(Bounds);
			found = false;
			Renderer[] componentsInChildren = target.GetComponentsInChildren<Renderer>(false);
			foreach (Renderer val in componentsInChildren)
			{
				if (val.enabled && (val is MeshRenderer || val is SkinnedMeshRenderer))
				{
					if (!found)
					{
						result = val.bounds;
						found = true;
					}
					else
					{
						((Bounds)(ref result)).Encapsulate(val.bounds);
					}
				}
			}
			return result;
		}
	}
	public readonly struct HighlightOptions
	{
		public float Duration { get; }

		public bool Light { get; }

		public bool Glow { get; }

		public HighlightOptions(float duration, bool light, bool glow)
		{
			Duration = duration;
			Light = light;
			Glow = glow;
		}
	}
}
namespace TidyChests.Containers
{
	internal static class ContainerAccess
	{
		public static bool CanStashInto(Container container, long playerId, bool multiUserChest, out string reason)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			reason = "";
			if ((Object)(object)container == (Object)null || container.GetInventory() == null)
			{
				reason = "not initialised";
				return false;
			}
			ZNetView nview = container.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				reason = "no network object";
				return false;
			}
			Player component = ((Component)nview).GetComponent<Player>();
			if ((Object)(object)component != (Object)null && (Object)(object)component != (Object)(object)Player.m_localPlayer)
			{
				reason = "carried by another player";
				return false;
			}
			if (container.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false))
			{
				reason = "inside someone else's ward";
				return false;
			}
			if (!container.CheckAccess(playerId))
			{
				reason = "private chest of another player";
				return false;
			}
			if (IsOpenByLocalPlayer(container) || multiUserChest)
			{
				return true;
			}
			if (container.IsInUse() || nview.GetZDO().GetInt(ZDOVars.s_inUse, 0) == 1)
			{
				reason = "in use by another player";
				return false;
			}
			if ((Object)(object)container.m_wagon != (Object)null && container.m_wagon.InUse())
			{
				reason = "cart in use";
				return false;
			}
			if (!IsAlone() && (Object)(object)((Component)container).GetComponentInParent<Ship>() != (Object)null)
			{
				reason = "ship cargo in multiplayer without MultiUserChest";
				return false;
			}
			return true;
		}

		public static bool PrepareForWrite(Container container, bool multiUserChest)
		{
			if (multiUserChest)
			{
				return true;
			}
			container.Load();
			container.m_nview.ClaimOwnership();
			return container.m_nview.IsOwner();
		}

		public static bool IsOpenByLocalPlayer(Container container)
		{
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance != (Object)null)
			{
				return (Object)(object)instance.m_currentContainer == (Object)(object)container;
			}
			return false;
		}

		public static string NameOf(Container container)
		{
			Inventory inventory = container.GetInventory();
			if (inventory != null)
			{
				return Localization.instance.Localize(inventory.GetName());
			}
			return ((Object)container).name;
		}

		private static bool IsAlone()
		{
			ZNet instance = ZNet.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				return instance.GetNrOfPlayers() <= 1;
			}
			return true;
		}
	}
	internal static class ContainerRegistry
	{
		private static readonly List<Container> All = new List<Container>();

		public static int Count => All.Count;

		public static void Add(Container container)
		{
			if ((Object)(object)container != (Object)null && !All.Contains(container))
			{
				All.Add(container);
			}
		}

		public static void Remove(Container container)
		{
			All.Remove(container);
		}

		public static void CollectNearby(Vector3 center, float radius, List<Container> result)
		{
			//IL_004c: 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_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)
			float num = radius * radius;
			Dictionary<Container, float> distances = new Dictionary<Container, float>();
			for (int num2 = All.Count - 1; num2 >= 0; num2--)
			{
				Container val = All[num2];
				if ((Object)(object)val == (Object)null)
				{
					All.RemoveAt(num2);
				}
				else
				{
					Vector3 val2 = ((Component)val).transform.position - center;
					float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
					if (sqrMagnitude <= num)
					{
						result.Add(val);
						distances[val] = sqrMagnitude;
					}
				}
			}
			result.Sort((Container a, Container b) => distances[a].CompareTo(distances[b]));
		}
	}
	[HarmonyPatch(typeof(Container), "Awake")]
	internal static class Container_Awake_Patch
	{
		private static void Postfix(Container __instance)
		{
			if (__instance.GetInventory() != null)
			{
				ContainerRegistry.Add(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Container), "OnDestroyed")]
	internal static class Container_OnDestroyed_Patch
	{
		private static void Postfix(Container __instance)
		{
			ContainerRegistry.Remove(__instance);
		}
	}
}
namespace TidyChests.Compat
{
	internal static class ChestMods
	{
		public const string MultiUserChestGuid = "com.maxsch.valheim.MultiUserChest";

		public static bool MultiUserChestLoaded => Chainloader.PluginInfos.ContainsKey("com.maxsch.valheim.MultiUserChest");

		public static void Report()
		{
			if (MultiUserChestLoaded)
			{
				string text = Chainloader.PluginInfos["com.maxsch.valheim.MultiUserChest"].Metadata.Version.ToString();
				Plugin.Log.LogInfo((object)("MultiUserChest " + text + " found: chests in use by other players are stashed into through its requests"));
			}
		}
	}
	internal static class SlotMods
	{
		internal abstract class Provider
		{
			private bool _resolved;

			private Func<ItemData, bool>? _classifier;

			public string Name { get; }

			public string PluginGuid { get; }

			public string SlotDescription { get; }

			public bool IsLoaded => Chainloader.PluginInfos.ContainsKey(PluginGuid);

			public string LoadedVersion
			{
				get
				{
					if (!Chainloader.PluginInfos.TryGetValue(PluginGuid, out var value) || ((value != null) ? value.Metadata : null) == null)
					{
						return "?";
					}
					return value.Metadata.Version.ToString();
				}
			}

			public Func<ItemData, bool>? Classifier
			{
				get
				{
					if (_resolved)
					{
						return _classifier;
					}
					_resolved = true;
					try
					{
						Assembly assembly = PluginAssembly();
						_classifier = ((assembly == null) ? null : CreateClassifier(assembly));
					}
					catch (Exception ex)
					{
						Plugin.Log.LogWarning((object)(Name + " API could not be resolved: " + ex.Message));
						_classifier = null;
					}
					if (_classifier == null)
					{
						Plugin.Log.LogWarning((object)(Name + " " + LoadedVersion + " is installed but its API was not found or has changed. Update " + Name + "; its slots are not protected from stashing."));
					}
					else
					{
						Plugin.Log.LogInfo((object)("Slot support: " + Name + " " + LoadedVersion + " (" + SlotDescription + " are never stashed)"));
					}
					return _classifier;
				}
			}

			protected Provider(string name, string pluginGuid, string slotDescription)
			{
				Name = name;
				PluginGuid = pluginGuid;
				SlotDescription = slotDescription;
			}

			protected abstract Func<ItemData, bool>? CreateClassifier(Assembly assembly);

			protected static MethodInfo? PublicStatic(Type api, string name, params Type[] parameters)
			{
				return api.GetMethod(name, BindingFlags.Static | BindingFlags.Public, null, parameters, null);
			}

			private Assembly? PluginAssembly()
			{
				if (Chainloader.PluginInfos.TryGetValue(PluginGuid, out var value) && (Object)(object)((value != null) ? value.Instance : null) != (Object)null)
				{
					return ((object)value.Instance).GetType().Assembly;
				}
				return null;
			}
		}

		private sealed class ExtraSlotsProvider : Provider
		{
			public ExtraSlotsProvider()
				: base("Extra Slots", "shudnal.ExtraSlots", "quick, food, ammo, misc and equipment slots")
			{
			}

			protected override Func<ItemData, bool>? CreateClassifier(Assembly assembly)
			{
				Type type = assembly.GetType("ExtraSlots.API", throwOnError: false);
				MethodInfo isSlotPosition = ((type == null) ? null : Provider.PublicStatic(type, "IsGridPositionASlot", typeof(Vector2i)));
				if (isSlotPosition == null)
				{
					return null;
				}
				return delegate(ItemData item)
				{
					//IL_0010: Unknown result type (might be due to invalid IL or missing references)
					object obj = isSlotPosition.Invoke(null, new object[1] { item.m_gridPos });
					return obj is bool && (bool)obj;
				};
			}
		}

		private sealed class EquipmentAndQuickSlotsProvider : Provider
		{
			public EquipmentAndQuickSlotsProvider()
				: base("EquipmentAndQuickSlots", "randyknapp.mods.equipmentandquickslots", "equipment and quick slots")
			{
			}

			protected override Func<ItemData, bool>? CreateClassifier(Assembly assembly)
			{
				Type type = assembly.GetType("EquipmentAndQuickSlots.API", throwOnError: false);
				MethodInfo isSlotCell = ((type == null) ? null : Provider.PublicStatic(type, "IsSlotCell", typeof(int), typeof(int), typeof(string).MakeByRefType()));
				if (isSlotCell == null)
				{
					return null;
				}
				return delegate(ItemData item)
				{
					object obj = isSlotCell.Invoke(null, new object[3]
					{
						item.m_gridPos.x,
						item.m_gridPos.y,
						null
					});
					return obj is bool && (bool)obj;
				};
			}
		}

		private sealed class BetterArcheryProvider : Provider
		{
			public BetterArcheryProvider()
				: base("Better Archery", "ishid4.mods.betterarchery", "quiver rows")
			{
			}

			protected override Func<ItemData, bool>? CreateClassifier(Assembly assembly)
			{
				Type type = assembly.GetTypes().FirstOrDefault((Type type2) => type2.IsClass && type2.Name == "BetterArchery");
				FieldInfo enabled = type?.GetField("ConfigQuiverEnabled", BindingFlags.Static | BindingFlags.Public);
				FieldInfo rowIndex = type?.GetField("QuiverRowIndex", BindingFlags.Static | BindingFlags.Public);
				if (enabled == null || rowIndex == null)
				{
					return null;
				}
				return delegate(ItemData item)
				{
					if (!(enabled.GetValue(null) is ConfigEntry<bool> val) || !val.Value || !(rowIndex.GetValue(null) is int num) || num <= 0)
					{
						return false;
					}
					return item.m_gridPos.y == num || item.m_gridPos.y == num - 1;
				};
			}
		}

		public const string ExtraSlotsGuid = "shudnal.ExtraSlots";

		public const string EquipmentAndQuickSlotsGuid = "randyknapp.mods.equipmentandquickslots";

		public const string BetterArcheryGuid = "ishid4.mods.betterarchery";

		private static readonly Provider[] Providers = new Provider[3]
		{
			new ExtraSlotsProvider(),
			new EquipmentAndQuickSlotsProvider(),
			new BetterArcheryProvider()
		};

		public static string SupportedMods => "Extra Slots, EquipmentAndQuickSlots 3.x, Better Archery";

		public static IEnumerable<Provider> Active => Providers.Where((Provider provider) => provider.IsLoaded);

		public static void Report()
		{
			bool flag = false;
			foreach (Provider item in Active)
			{
				flag = true;
				_ = item.Classifier;
			}
			if (!flag)
			{
				Plugin.Log.LogInfo((object)("No slot mod loaded (" + SupportedMods + "); only equipped and hotbar items are protected"));
			}
		}

		public static Func<ItemData, bool>? GetClassifier()
		{
			List<KeyValuePair<string, Func<ItemData, bool>>> classifiers = new List<KeyValuePair<string, Func<ItemData, bool>>>();
			foreach (Provider item in Active)
			{
				Func<ItemData, bool> classifier = item.Classifier;
				if (classifier != null)
				{
					classifiers.Add(new KeyValuePair<string, Func<ItemData, bool>>(item.Name, classifier));
				}
			}
			if (classifiers.Count == 0)
			{
				return null;
			}
			return delegate(ItemData item)
			{
				foreach (KeyValuePair<string, Func<ItemData, bool>> item2 in classifiers)
				{
					try
					{
						if (item2.Value(item))
						{
							return true;
						}
					}
					catch (Exception ex)
					{
						Plugin.Log.LogWarning((object)(item2.Key + " API call failed for " + item.m_shared.m_name + ", treating it as a regular item: " + ex.Message));
					}
				}
				return false;
			};
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}