Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of InventoryLink v0.2.5
InventoryLink.dll
Decompiled 3 hours agousing 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); } }