From 476651dc33025ea51f5b5ce0e0f4472c544cc170 Mon Sep 17 00:00:00 2001 From: Trident35ro <107998245+Trident35ro@users.noreply.github.com> Date: Wed, 5 Aug 2026 17:42:58 +0300 Subject: [PATCH] Add: Replacing/updating item actions Allows modders to handle, modify or replace the actions of items, from eating to attacking, allowing patching vanilla items with new behavior when used. --- .../Base/Base.GamePlugin.cs | 22 +++++++ HAModHelper.GamePlugin/Items/Items.Systems.cs | 60 +++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/HAModHelper.GamePlugin/Base/Base.GamePlugin.cs b/HAModHelper.GamePlugin/Base/Base.GamePlugin.cs index a7e5cfa..d0611de 100644 --- a/HAModHelper.GamePlugin/Base/Base.GamePlugin.cs +++ b/HAModHelper.GamePlugin/Base/Base.GamePlugin.cs @@ -282,5 +282,27 @@ public static void Prefix(Connection __instance) } } } + + [HarmonyPatch(typeof(inventory_ctr), "ON_DOUBLE_CLICK")] + private static class InventoryDoubleClickPatch + { + [HarmonyPrefix] + static bool Prefix(inventory_ctr __instance, object[] __args) + { + try + { + // extract data about the item for logic patching + int uiSlotId = (int)__args[0]; + string action = __args[1]?.ToString() ?? "null"; + + return ItemManager.Instance.HandleItemDoubleClick(__instance, uiSlotId, action); + } + catch (Exception ex) + { + HAMHMod.Logger.LogError($"[HAMH] Sniffer Patch Fail: {ex.Message}"); + return true; + } + } + } } diff --git a/HAModHelper.GamePlugin/Items/Items.Systems.cs b/HAModHelper.GamePlugin/Items/Items.Systems.cs index d2762b3..fcfa175 100644 --- a/HAModHelper.GamePlugin/Items/Items.Systems.cs +++ b/HAModHelper.GamePlugin/Items/Items.Systems.cs @@ -1,6 +1,7 @@ using HAModHelper.GamePlugin.Core; using HAModHelper.GamePlugin.Helpers; using HAModHelper.GamePlugin.Items.Interfaces; +using System.Reflection; namespace HAModHelper.GamePlugin.Items.Systems; @@ -133,6 +134,10 @@ public sealed class ItemManager private Dictionary _queuedItems = new(); private HashSet _removedBaseItems = new(); + // these 2 variables are used for intercepting item actions + public delegate bool ItemActionHandler(string itemName, string action, int slotId); + private List _interceptors = new(); + // TEST-ONLY: Spoof a fake ResourceControl for HAModHelper.Tests to use public IResourceControl? DebugResourceControlSource { get; set; } private ItemManager() { } @@ -286,4 +291,59 @@ public Dictionary ConvertItem(Item item) { return ItemConverter.ToGameFields(item); } + + public void RegisterInterceptor(ItemActionHandler handler) + { + _interceptors.Add(handler); + } + + public bool HandleItemDoubleClick(object inventoryCtr, int uiSlotId, string action) + { + int trueSlotId = GetMappedIndex(inventoryCtr, uiSlotId); + string itemName = GetItemName(inventoryCtr, trueSlotId); + + bool allowOriginal = true; // this tells the game whether to add the mod's logic on top of the original item logic (true) or to skip the original handling entirely (false) + + foreach (var handler in _interceptors) + { + if (!handler.Invoke(itemName, action, trueSlotId)) + { + allowOriginal = false; + } + } + + return allowOriginal; + } + + + // some helpers + private string GetItemName(object instance, int trueSlotId) + { + try + { + var invProp = instance.GetType().GetProperty("player_inventory"); + var playerInv = invProp?.GetValue(instance); + if (playerInv == null) return "Empty"; + + var getItem = playerInv.GetType().GetMethod("get_Item", [typeof(int)]); + var itemPair = getItem?.Invoke(playerInv, [trueSlotId]); + var item = itemPair?.GetType().GetProperty("item")?.GetValue(itemPair); + return item?.GetType().GetProperty("item_name")?.GetValue(item)?.ToString() ?? "Empty"; + } + catch { return "Empty"; } + } + + private int GetMappedIndex(object instance, int uiSlotId) + { + try + { + var method = instance.GetType().GetMethod("GetTrueInventoryIndex", (System.Reflection.BindingFlags)62); + if (method != null) return (int)method.Invoke(instance, [uiSlotId]); + + var field = instance.GetType().GetField("current_page") ?? instance.GetType().GetField("page_index"); + if (field != null) return ((int)field.GetValue(instance) * 15) + uiSlotId; + } + catch { } + return uiSlotId; + } } \ No newline at end of file