From 545725d3804cccc579bbc16bb11bedd49d9b81e8 Mon Sep 17 00:00:00 2001 From: CactusPie Date: Sun, 14 Jan 2024 20:28:59 +0100 Subject: [PATCH] Automatically merge stacks (money, ammo, etc.) when quickly transferring items to containers not marked with a @loot tag --- .../ContainerQuickLootPlugin.cs | 24 +++- .../QuickTransferPatch.cs | 108 ++++++++++++++---- 2 files changed, 102 insertions(+), 30 deletions(-) diff --git a/src/CactusPie.ContainerQuickLoot/ContainerQuickLootPlugin.cs b/src/CactusPie.ContainerQuickLoot/ContainerQuickLootPlugin.cs index d5e5b39..6e5e69b 100644 --- a/src/CactusPie.ContainerQuickLoot/ContainerQuickLootPlugin.cs +++ b/src/CactusPie.ContainerQuickLoot/ContainerQuickLootPlugin.cs @@ -8,16 +8,18 @@ namespace CactusPie.ContainerQuickLoot public class ContainerQuickLootPlugin : BaseUnityPlugin { internal static ConfigEntry EnableForCtrlClick { get; set; } - + internal static ConfigEntry EnableForLooseLoot { get; set; } - + internal static ConfigEntry AutoMergeStacks { get; set; } + internal static ConfigEntry AutoMergeStacksForNonLootContainers { get; set; } + [UsedImplicitly] internal void Start() { const string sectionName = "Container quick loot setting"; - + EnableForCtrlClick = Config.Bind ( sectionName, @@ -28,7 +30,7 @@ namespace CactusPie.ContainerQuickLoot "Automatically put the items in containers while transferring them with ctrl+click" ) ); - + EnableForLooseLoot = Config.Bind ( sectionName, @@ -39,7 +41,7 @@ namespace CactusPie.ContainerQuickLoot "Automatically put loose loot in containers" ) ); - + AutoMergeStacks = Config.Bind ( sectionName, @@ -51,6 +53,18 @@ namespace CactusPie.ContainerQuickLoot ) ); + AutoMergeStacksForNonLootContainers = Config.Bind + ( + sectionName, + "Merge stacks for non-loot containers", + true, + new ConfigDescription + ( + "Automatically merge stacks (money, ammo, etc.) when quickly transferring items to " + + "containers not marked with a @loot tag" + ) + ); + new QuickTransferPatch().Enable(); } } diff --git a/src/CactusPie.ContainerQuickLoot/QuickTransferPatch.cs b/src/CactusPie.ContainerQuickLoot/QuickTransferPatch.cs index 355d77d..3ef782b 100644 --- a/src/CactusPie.ContainerQuickLoot/QuickTransferPatch.cs +++ b/src/CactusPie.ContainerQuickLoot/QuickTransferPatch.cs @@ -13,7 +13,7 @@ namespace CactusPie.ContainerQuickLoot public class QuickTransferPatch : ModulePatch { private static readonly Regex LootTagRegex = new Regex("@loot[0-9]*", RegexOptions.Compiled, TimeSpan.FromMilliseconds(100)); - + protected override MethodBase GetTargetMethod() { MethodInfo method = typeof(GClass2585).GetMethod("QuickFindAppropriatePlace", BindingFlags.Public | BindingFlags.Static); @@ -30,6 +30,8 @@ namespace CactusPie.ContainerQuickLoot GClass2585.EMoveItemOrder order, bool simulate) { + Inventory inventory; + // If is ctrl+click loot if (order == GClass2585.EMoveItemOrder.MoveToAnotherSide) { @@ -38,51 +40,44 @@ namespace CactusPie.ContainerQuickLoot return true; } } - // If is loose loot pick up else if (order == GClass2585.EMoveItemOrder.PickUp && controller.OwnerType == EOwnerType.Profile) { if (!ContainerQuickLootPlugin.EnableForLooseLoot.Value) { - return true; + if (!TryGetInventory(out inventory)) + { + return true; + } + + return !TryMergeItemIntoAnExistingStack(item, inventory, controller, simulate, ref __result); } } else { return true; } - - GameWorld gameWorld = Singleton.Instance; - // If gameWorld is null that means the game is currently not in progress, for instance you're in your hideout - if (gameWorld == null) - { - return true; - } - // This check needs to be done only in game - otherwise we will not be able to receive quest rewards! if (item.QuestItem) { return true; } - - Player player = GetLocalPlayerFromWorld(gameWorld); - var inventory = (Inventory)typeof(Player).GetProperty("Inventory", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(player); - - if (inventory == null) + + if (!TryGetInventory(out inventory)) { return true; } - + IEnumerable targetContainers = FindTargetContainers(item, inventory); foreach (IContainer collectionContainer in targetContainers) { if (!(collectionContainer is GClass2318 container)) { - return true; + return !TryMergeItemIntoAnExistingStack(item, inventory, controller, simulate, ref __result); } - + // ReSharper disable once PossibleMultipleEnumeration if (!(targets.SingleOrDefaultWithoutException() is EquipmentClass)) { @@ -97,18 +92,17 @@ namespace CactusPie.ContainerQuickLoot { continue; } - + if (containedItem.Key.StackObjectsCount + item.StackObjectsCount > item.StackMaxSize) { continue; } - + GStruct375 mergeResult = GClass2585.Merge(item, containedItem.Key, controller, simulate); __result = new GStruct375(mergeResult.Value); return false; } } - GClass2580 location = container.FindLocationForItem(item); if (location == null) @@ -126,7 +120,32 @@ namespace CactusPie.ContainerQuickLoot { __result = moveResult.Cast(); } - + + return false; + } + + return !TryMergeItemIntoAnExistingStack(item, inventory, controller, simulate, ref __result); + } + + private static bool TryGetInventory(out Inventory inventory) + { + GameWorld gameWorld = Singleton.Instance; + + // If gameWorld is null that means the game is currently not in progress, for instance you're in your hideout + if (gameWorld == null) + { + inventory = null; + return false; + } + + Player player = GetLocalPlayerFromWorld(gameWorld); + + inventory = (Inventory)typeof(Player) + .GetProperty("Inventory", BindingFlags.NonPublic | BindingFlags.Instance) + ?.GetValue(player); + + if (inventory == null) + { return false; } @@ -136,7 +155,7 @@ namespace CactusPie.ContainerQuickLoot private static IEnumerable FindTargetContainers(Item item, Inventory inventory) { var matchingContainerCollections = new List<(ContainerCollection containerCollection, int priority)>(); - + foreach (Item inventoryItem in inventory.Equipment.GetAllItems()) { // It has to be a container collection - an item that we can transfer the loot into @@ -173,7 +192,7 @@ namespace CactusPie.ContainerQuickLoot string priorityString = regexMatch.Value.Substring(lootTagLength); int priority = priorityString.Length == 0 ? 0 : int.Parse(priorityString); - + matchingContainerCollections.Add((containerCollection, priority)); } @@ -184,6 +203,45 @@ namespace CactusPie.ContainerQuickLoot return result; } + // If there are not matching @loot containers found, we will try to merge the item into an existing stack + // anyway - but only if this behavior is enabled in the config + private static bool TryMergeItemIntoAnExistingStack( + Item item, + Inventory inventory, + TraderControllerClass controller, + bool simulate, + ref GStruct375 result) + { + if (!ContainerQuickLootPlugin.AutoMergeStacksForNonLootContainers.Value) + { + return false; + } + + if (item.Template.StackMaxSize <= 1) + { + return false; + } + + foreach (Item targetItem in inventory.Equipment.GetAllItems()) + { + if (targetItem.Template._id != item.Template._id) + { + continue; + } + + if (targetItem.StackObjectsCount + item.StackObjectsCount > item.Template.StackMaxSize) + { + continue; + } + + GStruct375 mergeResult = GClass2585.Merge(item, targetItem, controller, simulate); + result = new GStruct375(mergeResult.Value); + return true; + } + + return false; + } + private static Player GetLocalPlayerFromWorld(GameWorld gameWorld) { if (gameWorld == null || gameWorld.MainPlayer == null)