Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions HAModHelper.GamePlugin/Base/Base.GamePlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
}

60 changes: 60 additions & 0 deletions HAModHelper.GamePlugin/Items/Items.Systems.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -133,6 +134,10 @@ public sealed class ItemManager
private Dictionary<string, Item> _queuedItems = new();
private HashSet<string> _removedBaseItems = new();

// these 2 variables are used for intercepting item actions
public delegate bool ItemActionHandler(string itemName, string action, int slotId);
private List<ItemActionHandler> _interceptors = new();

// TEST-ONLY: Spoof a fake ResourceControl for HAModHelper.Tests to use
public IResourceControl? DebugResourceControlSource { get; set; }
private ItemManager() { }
Expand Down Expand Up @@ -286,4 +291,59 @@ public Dictionary<string, string> 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;
}
}