Decompiled source of InventoryLink v0.2.5

InventoryLink.dll

Decompiled 3 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("InventoryLink")]
[assembly: AssemblyFileVersion("0.2.5")]
[assembly: AssemblyCompany("R4V9N1")]
[assembly: AssemblyDescription("Created by R4V9N1")]
[assembly: AssemblyProduct("InventoryLink")]
[assembly: AssemblyCopyright("Created by R4V9N1")]
[assembly: AssemblyMetadata("Creator", "Created by R4V9N1")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.2.5.0")]
namespace InventoryLink;

[BepInPlugin("r4v9n1.inventorylink", "InventoryLink", "0.2.5")]
public sealed class InventoryLinkPlugin : BaseUnityPlugin
{
	internal sealed class RequirementCheckContext
	{
		public readonly Player Player;

		public readonly Inventory PlayerInventory;

		public readonly bool CountAmounts;

		public readonly bool CountAny;

		public RequirementCheckContext(Player player, Inventory playerInventory, bool countAmounts, bool countAny)
		{
			Player = player;
			PlayerInventory = playerInventory;
			CountAmounts = countAmounts;
			CountAny = countAny;
		}
	}

	internal sealed class RequirementDisplayContext
	{
		public readonly Player Player;

		public readonly Inventory PlayerInventory;

		public RequirementDisplayContext(Player player, Inventory playerInventory)
		{
			Player = player;
			PlayerInventory = playerInventory;
		}
	}

	internal sealed class PullPlan
	{
		public readonly Player Player;

		public readonly List<PullItem> Items = new List<PullItem>();

		public bool HasItems => Items.Count > 0;

		public PullPlan(Player player)
		{
			Player = player;
		}

		public void Add(string name, int quality, int amount)
		{
			for (int i = 0; i < Items.Count; i++)
			{
				if (Items[i].Name == name && Items[i].Quality == quality)
				{
					Items[i].Amount += amount;
					return;
				}
			}
			Items.Add(new PullItem(name, quality, amount));
		}
	}

	internal sealed class PullItem
	{
		public readonly string Name;

		public readonly int Quality;

		public int Amount;

		public PullItem(string name, int quality, int amount)
		{
			Name = name;
			Quality = quality;
			Amount = amount;
		}
	}

	private sealed class ContainerCandidate
	{
		public readonly Container Container;

		public readonly float DistanceSqr;

		public ContainerCandidate(Container container, float distanceSqr)
		{
			Container = container;
			DistanceSqr = distanceSqr;
		}
	}

	public const string PluginGuid = "r4v9n1.inventorylink";

	public const string PluginName = "InventoryLink";

	public const string PluginVersion = "0.2.5";

	public const string CreatorCredit = "Created by R4V9N1";

	private const string PreviousDefaultCraftingStationNames = "$piece_workbench,$piece_stonecutter";

	private const string DefaultCraftingStationNames = "$piece_workbench,$piece_stonecutter,$piece_forge,$piece_blackforge,$piece_magetable,$piece_artisanstation";

	private static ConfigEntry<bool> _enabled;

	private static ConfigEntry<float> _pullRadius;

	private static ConfigEntry<bool> _extendCraftingStationBuildRange;

	private static ConfigEntry<float> _craftingStationBuildRange;

	private static ConfigEntry<string> _craftingStationBuildRangeNames;

	private static ConfigEntry<bool> _respectWards;

	private static ConfigEntry<bool> _respectContainerAccess;

	private static ConfigEntry<bool> _skipInUseContainers;

	private static ConfigEntry<bool> _logPulls;

	private static ManualLogSource _log;

	private static FieldInfo _containerNViewField;

	private static FieldInfo _inventoryGuiCraftRecipeField;

	private static MethodInfo _containerLoadMethod;

	private static float _nextContainerFailureLogTime;

	private static int _suppressedContainerFailures;

	private static RequirementCheckContext _requirementCheck;

	private static RequirementDisplayContext _requirementDisplay;

	private static Player _lastPlacedPlayer;

	private static int _lastPlacedFrame = -1;

	private static Player _craftingPlayer;

	private static int _craftingFrame = -1;

	private static Player _cachedPlayer;

	private static int _cachedFrame = -1;

	private static List<ContainerCandidate> _cachedContainers;

	private Harmony _harmony;

	private void Awake()
	{
		//IL_0190: Unknown result type (might be due to invalid IL or missing references)
		//IL_019a: Expected O, but got Unknown
		_log = ((BaseUnityPlugin)this).Logger;
		_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable InventoryLink.");
		_pullRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "PullRadius", 50f, "How far from the player to search for linked containers.");
		_extendCraftingStationBuildRange = ((BaseUnityPlugin)this).Config.Bind<bool>("Crafting Stations", "ExtendBuildRange", true, "Extend configured crafting station build radii.");
		_craftingStationBuildRange = ((BaseUnityPlugin)this).Config.Bind<float>("Crafting Stations", "BuildRange", 50f, "Minimum build radius for configured crafting stations.");
		_craftingStationBuildRangeNames = ((BaseUnityPlugin)this).Config.Bind<string>("Crafting Stations", "StationNames", "$piece_workbench,$piece_stonecutter,$piece_forge,$piece_blackforge,$piece_magetable,$piece_artisanstation", "Comma-separated CraftingStation m_name values to extend. Defaults to the workbench, stonecutter, forge, black forge, Galdr table, and artisan table.");
		_respectWards = ((BaseUnityPlugin)this).Config.Bind<bool>("Access", "RespectWards", true, "Skip containers blocked by ward/private-area access.");
		_respectContainerAccess = ((BaseUnityPlugin)this).Config.Bind<bool>("Access", "RespectContainerAccess", true, "Respect personal/private container access rules.");
		_skipInUseContainers = ((BaseUnityPlugin)this).Config.Bind<bool>("Access", "SkipInUseContainers", true, "Do not pull from containers that another player has open.");
		_logPulls = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogPulls", false, "Log each successful item pull to the BepInEx log.");
		MigrateOldDefaults();
		_containerNViewField = AccessTools.Field(typeof(Container), "m_nview");
		_inventoryGuiCraftRecipeField = AccessTools.Field(typeof(InventoryGui), "m_craftRecipe");
		_containerLoadMethod = AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null);
		_harmony = new Harmony("r4v9n1.inventorylink");
		_harmony.PatchAll();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"InventoryLink 0.2.5 loaded.");
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Created by R4V9N1.");
	}

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

	internal static bool IsEnabledFor(Player player)
	{
		if (_enabled == null || !_enabled.Value || (Object)(object)player == (Object)null)
		{
			return false;
		}
		if (!((Object)(object)Player.m_localPlayer == (Object)null))
		{
			return Player.m_localPlayer == player;
		}
		return true;
	}

	internal static void ExtendCraftingStationBuildRange(CraftingStation station, ref float range)
	{
		if (_enabled != null && _enabled.Value && _extendCraftingStationBuildRange != null && _extendCraftingStationBuildRange.Value && !((Object)(object)station == (Object)null) && IsConfiguredCraftingStation(station))
		{
			float num = ((_craftingStationBuildRange == null) ? 50f : Math.Max(0f, _craftingStationBuildRange.Value));
			if (range < num)
			{
				range = num;
			}
		}
	}

	internal static void BeginRequirementCheck(Player player, RequirementMode mode)
	{
		//IL_0009: 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_001a: Invalid comparison between Unknown and I4
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Invalid comparison between Unknown and I4
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Invalid comparison between Unknown and I4
		if (IsEnabledFor(player) && ((int)mode == 0 || (int)mode == 2))
		{
			_requirementCheck = new RequirementCheckContext(player, ((Humanoid)player).GetInventory(), (int)mode == 0, (int)mode == 2);
		}
	}

	internal static void EndRequirementCheck()
	{
		_requirementCheck = null;
	}

	internal static void BeginRecipeRequirementCheck(Player player, Recipe recipe, bool discover)
	{
		if (!(!IsEnabledFor(player) || discover) && SupportsRecipeAutoPull(recipe))
		{
			_requirementCheck = new RequirementCheckContext(player, ((Humanoid)player).GetInventory(), countAmounts: true, countAny: false);
		}
	}

	internal static void BeginRequirementDisplay(Player player)
	{
		if (IsEnabledFor(player))
		{
			_requirementDisplay = new RequirementDisplayContext(player, ((Humanoid)player).GetInventory());
		}
	}

	internal static void EndRequirementDisplay()
	{
		_requirementDisplay = null;
	}

	internal static void BeginCrafting(InventoryGui inventoryGui, Player player)
	{
		if (IsEnabledFor(player) && SupportsRecipeAutoPull(GetCraftRecipe(inventoryGui)))
		{
			_craftingPlayer = player;
			_craftingFrame = Time.frameCount;
		}
	}

	internal static void EndCrafting()
	{
		_craftingPlayer = null;
		_craftingFrame = -1;
	}

	internal static void AddNearbyCountForRequirement(Inventory inventory, string itemName, int quality, bool matchWorldLevel, ref int count)
	{
		Player val = null;
		Inventory val2 = null;
		if (_requirementCheck != null && _requirementCheck.CountAmounts)
		{
			val = _requirementCheck.Player;
			val2 = _requirementCheck.PlayerInventory;
		}
		else
		{
			if (_requirementDisplay == null)
			{
				return;
			}
			val = _requirementDisplay.Player;
			val2 = _requirementDisplay.PlayerInventory;
		}
		if (inventory == val2)
		{
			count += CountNearbyItem(val, itemName, quality, matchWorldLevel, int.MaxValue);
		}
	}

	internal static void AddNearbyHaveItemForRequirement(Inventory inventory, string itemName, bool matchWorldLevel, ref bool haveItem)
	{
		if (!haveItem && _requirementCheck != null && _requirementCheck.CountAny && inventory == _requirementCheck.PlayerInventory)
		{
			haveItem = CountNearbyItem(_requirementCheck.Player, itemName, -1, matchWorldLevel, 1) > 0;
		}
	}

	internal static void MarkPlaced(Player player, bool placed)
	{
		if (placed && IsEnabledFor(player))
		{
			_lastPlacedPlayer = player;
			_lastPlacedFrame = Time.frameCount;
		}
	}

	internal static PullPlan CreatePullPlan(Player player, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier)
	{
		bool flag = _lastPlacedPlayer == player && _lastPlacedFrame == Time.frameCount;
		bool flag2 = _craftingPlayer == player && _craftingFrame == Time.frameCount;
		if (!IsEnabledFor(player) || (!flag && !flag2))
		{
			return null;
		}
		if (requirements == null || requirements.Length == 0)
		{
			return null;
		}
		Inventory inventory = ((Humanoid)player).GetInventory();
		if (inventory == null)
		{
			return null;
		}
		PullPlan pullPlan = new PullPlan(player);
		foreach (Requirement val in requirements)
		{
			if (val == null || (Object)(object)val.m_resItem == (Object)null)
			{
				continue;
			}
			int num = val.GetAmount(qualityLevel) * multiplier;
			if (num > 0)
			{
				string name = val.m_resItem.m_itemData.m_shared.m_name;
				int num2 = inventory.CountItems(name, itemQuality, true);
				int num3 = num - num2;
				if (num3 > 0)
				{
					pullPlan.Add(name, itemQuality, num3);
				}
			}
		}
		if (!pullPlan.HasItems)
		{
			return null;
		}
		return pullPlan;
	}

	internal static void PayPullPlan(PullPlan plan)
	{
		if (plan == null || !IsEnabledFor(plan.Player))
		{
			return;
		}
		List<ContainerCandidate> list = FindContainers(plan.Player);
		for (int i = 0; i < plan.Items.Count; i++)
		{
			PullItem pullItem = plan.Items[i];
			int num = pullItem.Amount;
			for (int j = 0; j < list.Count; j++)
			{
				if (num <= 0)
				{
					break;
				}
				num -= PullFromContainer(list[j].Container, pullItem.Name, pullItem.Quality, num);
			}
			if (num > 0 && _log != null)
			{
				_log.LogWarning((object)("InventoryLink could not pull " + num + "x " + pullItem.Name + " after placement. The container contents may have changed."));
			}
		}
		ClearContainerCache();
	}

	private static int CountNearbyItem(Player player, string itemName, int quality, bool matchWorldLevel, int stopAt)
	{
		if (string.IsNullOrEmpty(itemName) || !IsEnabledFor(player))
		{
			return 0;
		}
		int num = 0;
		List<ContainerCandidate> list = FindContainers(player);
		for (int i = 0; i < list.Count; i++)
		{
			Inventory loadedInventory = GetLoadedInventory(list[i].Container);
			if (loadedInventory != null)
			{
				num += loadedInventory.CountItems(itemName, quality, matchWorldLevel);
				if (num >= stopAt)
				{
					return num;
				}
			}
		}
		return num;
	}

	private static int PullFromContainer(Container container, string itemName, int quality, int amount)
	{
		if ((Object)(object)container == (Object)null || amount <= 0)
		{
			return 0;
		}
		ZNetView containerView = GetContainerView(container);
		if ((Object)(object)containerView != (Object)null && containerView.IsValid() && !containerView.IsOwner())
		{
			containerView.ClaimOwnership();
		}
		Inventory loadedInventory = GetLoadedInventory(container);
		if (loadedInventory == null)
		{
			return 0;
		}
		int num = loadedInventory.CountItems(itemName, quality, true);
		int num2 = Math.Min(num, amount);
		if (num2 <= 0)
		{
			return 0;
		}
		loadedInventory.RemoveItem(itemName, num2, quality, true);
		int num3 = loadedInventory.CountItems(itemName, quality, true);
		int num4 = num - num3;
		if (num4 > 0 && _log != null && _logPulls != null && _logPulls.Value)
		{
			_log.LogInfo((object)("Pulled " + num4 + "x " + itemName + " from " + container.GetHoverName() + "."));
		}
		return Math.Max(num4, 0);
	}

	private static List<ContainerCandidate> FindContainers(Player player)
	{
		//IL_005c: 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_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: 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_0099: Unknown result type (might be due to invalid IL or missing references)
		if (_cachedContainers != null && _cachedPlayer == player && _cachedFrame == Time.frameCount)
		{
			return _cachedContainers;
		}
		List<ContainerCandidate> list = new List<ContainerCandidate>();
		if (!IsEnabledFor(player))
		{
			return list;
		}
		float num = ((_pullRadius == null) ? 50f : Math.Max(0f, _pullRadius.Value));
		float num2 = num * num;
		Vector3 position = ((Component)player).transform.position;
		long playerID = player.GetPlayerID();
		Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0);
		foreach (Container val in array)
		{
			if (!((Object)(object)val == (Object)null))
			{
				Vector3 val2 = ((Component)val).transform.position - position;
				float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
				if (!(sqrMagnitude > num2) && CanUseContainer(val, playerID))
				{
					list.Add(new ContainerCandidate(val, sqrMagnitude));
				}
			}
		}
		list.Sort(delegate(ContainerCandidate left, ContainerCandidate right)
		{
			float distanceSqr = left.DistanceSqr;
			return distanceSqr.CompareTo(right.DistanceSqr);
		});
		_cachedPlayer = player;
		_cachedFrame = Time.frameCount;
		_cachedContainers = list;
		return list;
	}

	private static bool CanUseContainer(Container container, long playerId)
	{
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (!TryGetReadyContainer(container, out var _))
			{
				return false;
			}
			if (_skipInUseContainers != null && _skipInUseContainers.Value && container.IsInUse())
			{
				return false;
			}
			if (_respectWards != null && _respectWards.Value && ContainerChecksGuardStone(container) && !PrivateArea.CheckAccess(((Component)container).transform.position, 0f, true, false))
			{
				return false;
			}
			if (_respectContainerAccess != null && _respectContainerAccess.Value && !CheckContainerAccess(container, playerId))
			{
				return false;
			}
			return GetLoadedInventory(container) != null;
		}
		catch (Exception exception)
		{
			LogUnexpectedContainerFailure(container, exception);
			return false;
		}
	}

	private static bool ContainerChecksGuardStone(Container container)
	{
		if ((Object)(object)container != (Object)null)
		{
			return container.m_checkGuardStone;
		}
		return false;
	}

	private static bool CheckContainerAccess(Container container, long playerId)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Expected I4, but got Unknown
		if ((Object)(object)container == (Object)null)
		{
			return false;
		}
		PrivacySetting privacy = container.m_privacy;
		switch ((int)privacy)
		{
		case 2:
			return true;
		case 0:
		{
			Piece component = ((Component)container).GetComponent<Piece>();
			if ((Object)(object)component != (Object)null)
			{
				return component.GetCreator() == playerId;
			}
			return false;
		}
		default:
			return false;
		}
	}

	private static Inventory GetLoadedInventory(Container container)
	{
		try
		{
			if (!TryGetReadyContainer(container, out var view))
			{
				return null;
			}
			LoadContainer(container);
			if (!view.IsValid())
			{
				return null;
			}
			return container.GetInventory();
		}
		catch (Exception exception)
		{
			LogUnexpectedContainerFailure(container, exception);
			return null;
		}
	}

	private static void LoadContainer(Container container)
	{
		if (_containerLoadMethod != null)
		{
			_containerLoadMethod.Invoke(container, null);
		}
	}

	private static ZNetView GetContainerView(Container container)
	{
		if (_containerNViewField == null || (Object)(object)container == (Object)null)
		{
			return null;
		}
		object? value = _containerNViewField.GetValue(container);
		return (ZNetView)((value is ZNetView) ? value : null);
	}

	private static bool TryGetReadyContainer(Container container, out ZNetView view)
	{
		view = null;
		if ((Object)(object)container == (Object)null || !((Behaviour)container).isActiveAndEnabled)
		{
			return false;
		}
		view = GetContainerView(container);
		if ((Object)(object)view != (Object)null && view.IsValid())
		{
			return container.GetInventory() != null;
		}
		return false;
	}

	private static void LogUnexpectedContainerFailure(Container container, Exception exception)
	{
		if (_log != null && exception != null)
		{
			while (exception is TargetInvocationException && exception.InnerException != null)
			{
				exception = exception.InnerException;
			}
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime < _nextContainerFailureLogTime)
			{
				_suppressedContainerFailures++;
				return;
			}
			string text = (((Object)(object)container == (Object)null) ? "<destroyed>" : ((Object)((Component)container).gameObject).name);
			string text2 = ((_suppressedContainerFailures > 0) ? (" (" + _suppressedContainerFailures + " similar failures suppressed)") : "");
			_suppressedContainerFailures = 0;
			_nextContainerFailureLogTime = unscaledTime + 5f;
			_log.LogDebug((object)("Skipped unavailable container '" + text + "': " + exception.GetType().Name + ": " + exception.Message + text2));
		}
	}

	private static Recipe GetCraftRecipe(InventoryGui inventoryGui)
	{
		if (_inventoryGuiCraftRecipeField == null || (Object)(object)inventoryGui == (Object)null)
		{
			return null;
		}
		object? value = _inventoryGuiCraftRecipeField.GetValue(inventoryGui);
		return (Recipe)((value is Recipe) ? value : null);
	}

	private static bool SupportsRecipeAutoPull(Recipe recipe)
	{
		if ((Object)(object)recipe != (Object)null)
		{
			return !recipe.m_requireOnlyOneIngredient;
		}
		return false;
	}

	private static bool IsConfiguredCraftingStation(CraftingStation station)
	{
		if ((Object)(object)station == (Object)null)
		{
			return false;
		}
		string text = ((_craftingStationBuildRangeNames == null) ? "$piece_workbench,$piece_stonecutter,$piece_forge,$piece_blackforge,$piece_magetable,$piece_artisanstation" : _craftingStationBuildRangeNames.Value);
		if (string.IsNullOrEmpty(text))
		{
			return false;
		}
		string a = station.m_name ?? "";
		string[] array = text.Split(new char[1] { ',' });
		for (int i = 0; i < array.Length; i++)
		{
			string text2 = array[i].Trim();
			if (text2.Length != 0 && string.Equals(a, text2, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
		}
		return false;
	}

	private void MigrateOldDefaults()
	{
		bool flag = false;
		if (_pullRadius != null && Math.Abs(_pullRadius.Value - 30f) < 0.001f)
		{
			_pullRadius.Value = 50f;
			flag = true;
		}
		if (_craftingStationBuildRangeNames != null && string.Equals(_craftingStationBuildRangeNames.Value, "$piece_workbench,$piece_stonecutter", StringComparison.Ordinal))
		{
			_craftingStationBuildRangeNames.Value = "$piece_workbench,$piece_stonecutter,$piece_forge,$piece_blackforge,$piece_magetable,$piece_artisanstation";
			flag = true;
		}
		if (flag)
		{
			((BaseUnityPlugin)this).Config.Save();
		}
	}

	private static void ClearContainerCache()
	{
		_cachedPlayer = null;
		_cachedFrame = -1;
		_cachedContainers = null;
	}
}
[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
{
	typeof(Piece),
	typeof(RequirementMode)
})]
internal static class PlayerHaveRequirementsPatch
{
	private static void Prefix(Player __instance, RequirementMode mode)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		InventoryLinkPlugin.BeginRequirementCheck(__instance, mode);
	}

	private static void Postfix()
	{
		InventoryLinkPlugin.EndRequirementCheck();
	}
}
[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
{
	typeof(Recipe),
	typeof(bool),
	typeof(int),
	typeof(int)
})]
internal static class PlayerHaveRecipeRequirementsPatch
{
	private static void Prefix(Player __instance, Recipe recipe, bool discover)
	{
		InventoryLinkPlugin.BeginRecipeRequirementCheck(__instance, recipe, discover);
	}

	private static void Postfix()
	{
		InventoryLinkPlugin.EndRequirementCheck();
	}
}
[HarmonyPatch(typeof(InventoryGui), "SetupRequirement", new Type[]
{
	typeof(Transform),
	typeof(Requirement),
	typeof(Player),
	typeof(bool),
	typeof(int),
	typeof(int)
})]
internal static class InventoryGuiSetupRequirementPatch
{
	private static void Prefix(Player player)
	{
		InventoryLinkPlugin.BeginRequirementDisplay(player);
	}

	private static void Postfix()
	{
		InventoryLinkPlugin.EndRequirementDisplay();
	}
}
[HarmonyPatch(typeof(InventoryGui), "DoCrafting", new Type[] { typeof(Player) })]
internal static class InventoryGuiDoCraftingPatch
{
	private static void Prefix(InventoryGui __instance, Player player)
	{
		InventoryLinkPlugin.BeginCrafting(__instance, player);
	}

	private static void Postfix()
	{
		InventoryLinkPlugin.EndCrafting();
	}
}
[HarmonyPatch(typeof(Inventory), "CountItems", new Type[]
{
	typeof(string),
	typeof(int),
	typeof(bool)
})]
internal static class InventoryCountItemsPatch
{
	private static void Postfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result)
	{
		InventoryLinkPlugin.AddNearbyCountForRequirement(__instance, name, quality, matchWorldLevel, ref __result);
	}
}
[HarmonyPatch(typeof(Inventory), "HaveItem", new Type[]
{
	typeof(string),
	typeof(bool)
})]
internal static class InventoryHaveItemPatch
{
	private static void Postfix(Inventory __instance, string name, bool matchWorldLevel, ref bool __result)
	{
		InventoryLinkPlugin.AddNearbyHaveItemForRequirement(__instance, name, matchWorldLevel, ref __result);
	}
}
[HarmonyPatch(typeof(CraftingStation), "GetStationBuildRange")]
internal static class CraftingStationGetStationBuildRangePatch
{
	private static void Postfix(CraftingStation __instance, ref float __result)
	{
		InventoryLinkPlugin.ExtendCraftingStationBuildRange(__instance, ref __result);
	}
}
[HarmonyPatch(typeof(Player), "TryPlacePiece", new Type[] { typeof(Piece) })]
internal static class PlayerTryPlacePiecePatch
{
	private static void Postfix(Player __instance, bool __result)
	{
		InventoryLinkPlugin.MarkPlaced(__instance, __result);
	}
}
[HarmonyPatch(typeof(Player), "ConsumeResources", new Type[]
{
	typeof(Requirement[]),
	typeof(int),
	typeof(int),
	typeof(int)
})]
internal static class PlayerConsumeResourcesPatch
{
	private static void Prefix(Player __instance, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier, ref InventoryLinkPlugin.PullPlan __state)
	{
		__state = InventoryLinkPlugin.CreatePullPlan(__instance, requirements, qualityLevel, itemQuality, multiplier);
	}

	private static void Postfix(InventoryLinkPlugin.PullPlan __state)
	{
		InventoryLinkPlugin.PayPullPlan(__state);
	}
}
[BepInPlugin("r4v9n1.inventorylink.autosort", "InventoryLink Auto Sort", "0.1.0")]
public sealed class InventorySortPlugin : BaseUnityPlugin
{
	[Serializable]
	[CompilerGenerated]
	private sealed class <>c
	{
		public static readonly <>c <>9 = new <>c();

		public static UnityAction <>9__18_0;

		public static UnityAction <>9__18_1;

		internal void <SetupButtons>b__18_0()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				SortInventory(((Humanoid)localPlayer).GetInventory());
			}
		}

		internal void <SetupButtons>b__18_1()
		{
			InventoryGui instance = InventoryGui.instance;
			Container val = (((Object)(object)instance != (Object)null) ? GetCurrentContainer(instance) : null);
			if ((Object)(object)val != (Object)null)
			{
				SortInventory(val.GetInventory());
			}
		}
	}

	public const string PluginGuid = "r4v9n1.inventorylink.autosort";

	public const string PluginName = "InventoryLink Auto Sort";

	public const string PluginVersion = "0.1.0";

	private static readonly Dictionary<ItemType, int> CategoryOrder = new Dictionary<ItemType, int>
	{
		{
			(ItemType)3,
			0
		},
		{
			(ItemType)14,
			0
		},
		{
			(ItemType)22,
			0
		},
		{
			(ItemType)4,
			0
		},
		{
			(ItemType)20,
			0
		},
		{
			(ItemType)5,
			1
		},
		{
			(ItemType)6,
			2
		},
		{
			(ItemType)7,
			2
		},
		{
			(ItemType)11,
			2
		},
		{
			(ItemType)17,
			2
		},
		{
			(ItemType)12,
			2
		},
		{
			(ItemType)19,
			3
		},
		{
			(ItemType)18,
			3
		},
		{
			(ItemType)15,
			3
		},
		{
			(ItemType)9,
			4
		},
		{
			(ItemType)23,
			4
		},
		{
			(ItemType)2,
			5
		},
		{
			(ItemType)21,
			5
		},
		{
			(ItemType)13,
			6
		},
		{
			(ItemType)10,
			7
		},
		{
			(ItemType)24,
			7
		},
		{
			(ItemType)1,
			8
		},
		{
			(ItemType)16,
			9
		},
		{
			(ItemType)0,
			9
		}
	};

	private static ConfigEntry<bool> _enabled;

	private static ConfigEntry<bool> _addSortButtons;

	private static ConfigEntry<KeyboardShortcut> _sortKey;

	private static ConfigEntry<float> _playerButtonOffsetY;

	private static ConfigEntry<float> _containerButtonOffsetY;

	private static ManualLogSource _log;

	private static Button _playerSortButton;

	private static Button _containerSortButton;

	private static FieldInfo _currentContainerField;

	private static MethodInfo _inventoryChangedMethod;

	private Harmony _harmony;

	private void Awake()
	{
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Expected O, but got Unknown
		_log = ((BaseUnityPlugin)this).Logger;
		_currentContainerField = AccessTools.Field(typeof(InventoryGui), "m_currentContainer");
		_inventoryChangedMethod = AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null);
		_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable auto-sort for the player inventory and open containers.");
		_addSortButtons = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AddSortButtons", true, "Clone vanilla buttons into 'Sort' buttons on the inventory/container panels.");
		_sortKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "SortKey", new KeyboardShortcut((KeyCode)120, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Key combo that sorts the player inventory, and the open container if any.");
		_playerButtonOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("General", "PlayerButtonOffsetY", 35f, "Vertical offset (UI units) of the cloned player-inventory sort button from the Stack All button it's cloned from.");
		_containerButtonOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ContainerButtonOffsetY", 35f, "Vertical offset (UI units) of the cloned container sort button from the Take All button it's cloned from.");
		_harmony = new Harmony("r4v9n1.inventorylink.autosort");
		_harmony.PatchAll();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"InventoryLink Auto Sort 0.1.0 loaded.");
	}

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

	private void Update()
	{
		//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)
		if (_enabled == null || !_enabled.Value || _sortKey == null)
		{
			return;
		}
		KeyboardShortcut value = _sortKey.Value;
		if (((KeyboardShortcut)(ref value)).IsDown())
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				SortInventory(((Humanoid)localPlayer).GetInventory());
			}
			InventoryGui instance = InventoryGui.instance;
			Container val = (((Object)(object)instance != (Object)null && instance.IsContainerOpen()) ? GetCurrentContainer(instance) : null);
			if ((Object)(object)val != (Object)null)
			{
				SortInventory(val.GetInventory());
			}
		}
	}

	internal static void SetupButtons(InventoryGui gui)
	{
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Expected O, but got Unknown
		//IL_0116: 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_0121: Expected O, but got Unknown
		if (_addSortButtons == null || !_addSortButtons.Value || (Object)(object)gui == (Object)null)
		{
			return;
		}
		if ((Object)(object)_playerSortButton == (Object)null)
		{
			if ((Object)(object)gui.m_stackAllButton == (Object)null)
			{
				if (_log != null)
				{
					_log.LogWarning((object)"Auto Sort: InventoryGui.m_stackAllButton was null; player sort button was not created.");
				}
			}
			else
			{
				float offsetY = ((_playerButtonOffsetY == null) ? 35f : _playerButtonOffsetY.Value);
				Button stackAllButton = gui.m_stackAllButton;
				object obj = <>c.<>9__18_0;
				if (obj == null)
				{
					UnityAction val = delegate
					{
						Player localPlayer = Player.m_localPlayer;
						if ((Object)(object)localPlayer != (Object)null)
						{
							SortInventory(((Humanoid)localPlayer).GetInventory());
						}
					};
					<>c.<>9__18_0 = val;
					obj = (object)val;
				}
				_playerSortButton = CloneButton(stackAllButton, offsetY, "Sort Inv.", "PlayerSortButton", (UnityAction)obj);
			}
		}
		if (!((Object)(object)_containerSortButton == (Object)null))
		{
			return;
		}
		if ((Object)(object)gui.m_takeAllButton == (Object)null)
		{
			if (_log != null)
			{
				_log.LogWarning((object)"Auto Sort: InventoryGui.m_takeAllButton was null; container sort button was not created.");
			}
			return;
		}
		float offsetY2 = ((_containerButtonOffsetY == null) ? 35f : _containerButtonOffsetY.Value);
		Button takeAllButton = gui.m_takeAllButton;
		object obj2 = <>c.<>9__18_1;
		if (obj2 == null)
		{
			UnityAction val2 = delegate
			{
				InventoryGui instance = InventoryGui.instance;
				Container val3 = (((Object)(object)instance != (Object)null) ? GetCurrentContainer(instance) : null);
				if ((Object)(object)val3 != (Object)null)
				{
					SortInventory(val3.GetInventory());
				}
			};
			<>c.<>9__18_1 = val2;
			obj2 = (object)val2;
		}
		_containerSortButton = CloneButton(takeAllButton, offsetY2, "Sort", "ContainerSortButton", (UnityAction)obj2);
		((Component)_containerSortButton).gameObject.SetActive(false);
	}

	private static Container GetCurrentContainer(InventoryGui gui)
	{
		if (_currentContainerField == null || (Object)(object)gui == (Object)null)
		{
			return null;
		}
		object? value = _currentContainerField.GetValue(gui);
		return (Container)((value is Container) ? value : null);
	}

	internal static void SetContainerButtonVisible(bool visible)
	{
		if ((Object)(object)_containerSortButton != (Object)null)
		{
			((Component)_containerSortButton).gameObject.SetActive(visible);
		}
	}

	private unsafe static Button CloneButton(Button template, float offsetY, string label, string cloneName, UnityAction onClick)
	{
		//IL_0055: 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_0065: 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_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = Object.Instantiate<GameObject>(((Component)template).gameObject, ((Component)template).transform.parent);
		((Object)val).name = cloneName;
		val.SetActive(true);
		LayoutElement val2 = val.GetComponent<LayoutElement>();
		if ((Object)(object)val2 == (Object)null)
		{
			val2 = val.AddComponent<LayoutElement>();
		}
		val2.ignoreLayout = true;
		RectTransform component = val.GetComponent<RectTransform>();
		if ((Object)(object)component != (Object)null)
		{
			component.anchoredPosition += new Vector2(0f, offsetY);
		}
		TMP_Text componentInChildren = val.GetComponentInChildren<TMP_Text>();
		if ((Object)(object)componentInChildren != (Object)null)
		{
			componentInChildren.text = label;
		}
		Button component2 = val.GetComponent<Button>();
		((UnityEventBase)component2.onClick).RemoveAllListeners();
		((UnityEvent)component2.onClick).AddListener(onClick);
		if (_log != null)
		{
			Vector2 val3 = (((Object)(object)component != (Object)null) ? component.anchoredPosition : Vector2.zero);
			ManualLogSource log = _log;
			string[] obj = new string[7] { "Auto Sort: created '", cloneName, "' at ", null, null, null, null };
			Vector2 val4 = val3;
			obj[3] = ((object)(*(Vector2*)(&val4))/*cast due to .constrained prefix*/).ToString();
			obj[4] = " (parent active=";
			obj[5] = ((Component)val.transform.parent).gameObject.activeInHierarchy.ToString();
			obj[6] = ").";
			log.LogInfo((object)string.Concat(obj));
		}
		return component2;
	}

	internal static void SortInventory(Inventory inventory)
	{
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
		if (inventory == null)
		{
			return;
		}
		int width = inventory.GetWidth();
		int height = inventory.GetHeight();
		if (width <= 0 || height <= 0)
		{
			return;
		}
		List<ItemData> allItems = inventory.GetAllItems();
		List<ItemData> list = new List<ItemData>();
		HashSet<int> hashSet = new HashSet<int>();
		for (int i = 0; i < allItems.Count; i++)
		{
			ItemData val = allItems[i];
			if (val != null && val.IsEquipable())
			{
				hashSet.Add(val.m_gridPos.y * width + val.m_gridPos.x);
			}
			else
			{
				list.Add(val);
			}
		}
		list.Sort(CompareItems);
		int num = 0;
		for (int j = 0; j < height; j++)
		{
			if (num >= list.Count)
			{
				break;
			}
			for (int k = 0; k < width; k++)
			{
				if (num >= list.Count)
				{
					break;
				}
				if (!hashSet.Contains(j * width + k))
				{
					list[num].m_gridPos = new Vector2i(k, j);
					num++;
				}
			}
		}
		if (_inventoryChangedMethod != null)
		{
			_inventoryChangedMethod.Invoke(inventory, null);
		}
		if (_log != null)
		{
			_log.LogInfo((object)("Sorted " + num + " item stack(s), left " + hashSet.Count + " equippable item(s) in place."));
		}
	}

	private static int CompareItems(ItemData a, ItemData b)
	{
		int num = GetCategoryOrder(a).CompareTo(GetCategoryOrder(b));
		if (num != 0)
		{
			return num;
		}
		string strA = ((a.m_shared != null) ? a.m_shared.m_name : string.Empty);
		string strB = ((b.m_shared != null) ? b.m_shared.m_name : string.Empty);
		int num2 = string.Compare(strA, strB, StringComparison.OrdinalIgnoreCase);
		if (num2 != 0)
		{
			return num2;
		}
		int num3 = b.m_quality.CompareTo(a.m_quality);
		if (num3 != 0)
		{
			return num3;
		}
		return b.m_stack.CompareTo(a.m_stack);
	}

	private static int GetCategoryOrder(ItemData item)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		if (item != null && item.m_shared != null && CategoryOrder.TryGetValue(item.m_shared.m_itemType, out var value))
		{
			return value;
		}
		return 9;
	}
}
[HarmonyPatch(typeof(InventoryGui), "Awake")]
internal static class InventoryGuiAwakeSortPatch
{
	private static void Postfix(InventoryGui __instance)
	{
		InventorySortPlugin.SetupButtons(__instance);
	}
}
[HarmonyPatch(typeof(InventoryGui), "Show", new Type[]
{
	typeof(Container),
	typeof(int)
})]
internal static class InventoryGuiShowSortPatch
{
	private static void Postfix()
	{
		InventorySortPlugin.SetContainerButtonVisible(visible: true);
	}
}
[HarmonyPatch(typeof(InventoryGui), "CloseContainer")]
internal static class InventoryGuiCloseContainerSortPatch
{
	private static void Postfix()
	{
		InventorySortPlugin.SetContainerButtonVisible(visible: false);
	}
}