empty slot linked search

This commit is contained in:
Tyfon
2024-07-10 17:31:33 -07:00
parent 395108eda8
commit 90a197afc9
10 changed files with 295 additions and 25 deletions

View File

@@ -0,0 +1,29 @@
using EFT.InventoryLogic;
using EFT.UI;
using EFT.UI.Ragfair;
using System;
using System.Collections.Generic;
namespace UIFixes.ContextMenus
{
public class EmptySlotMenu(Slot slot, ItemContextAbstractClass itemContext, ItemUiContext itemUiContext, Action closeAction) : GClass3042(itemContext, itemUiContext, closeAction)
{
private static readonly List<EItemInfoButton> Actions = [EItemInfoButton.LinkedSearch];
private readonly Slot slot = slot;
public override IEnumerable<EItemInfoButton> AvailableInteractions => Actions;
public override void ExecuteInteractionInternal(EItemInfoButton interaction)
{
switch (interaction)
{
case EItemInfoButton.LinkedSearch:
Search(new GClass3219(EFilterType.LinkedSearch, slot.ParentItem.TemplateId + ":" + slot.Id, true));
break;
default:
break;
}
}
}
}

View File

@@ -0,0 +1,82 @@
using EFT.InventoryLogic;
using EFT.UI;
using System;
using UnityEngine;
using UnityEngine.EventSystems;
namespace UIFixes.ContextMenus
{
public class EmptySlotMenuTrigger : MonoBehaviour, IPointerClickHandler, IPointerDownHandler, IPointerUpHandler, IPointerEnterHandler, IPointerExitHandler
{
private ItemUiContext itemUiContext;
private Slot slot;
private ItemContextAbstractClass parentContext;
private bool hovered = false;
public void Init(Slot slot, ItemContextAbstractClass parentContext, ItemUiContext itemUiContext)
{
this.itemUiContext = itemUiContext;
this.slot = slot;
this.parentContext = parentContext;
}
public void Update()
{
if (!hovered)
{
return;
}
if (Settings.LinkedSearchKeyBind.Value.IsDown())
{
using EmptySlotContext context = new(slot, parentContext, itemUiContext);
var interactions = itemUiContext.GetItemContextInteractions(context, null);
interactions.ExecuteInteraction(EItemInfoButton.LinkedSearch);
}
}
public void OnPointerClick(PointerEventData eventData)
{
if (eventData.button == PointerEventData.InputButton.Right)
{
EmptySlotContext context = new(slot, parentContext, itemUiContext);
itemUiContext.ShowContextMenu(context, eventData.position);
}
}
public void OnPointerDown(PointerEventData eventData) { }
public void OnPointerEnter(PointerEventData eventData)
{
hovered = true;
}
public void OnPointerExit(PointerEventData eventData)
{
hovered = false;
}
public void OnPointerUp(PointerEventData eventData) { }
}
public class EmptySlotContext(Slot slot, ItemContextAbstractClass parentContext, ItemUiContext itemUiContext) : ItemContextAbstractClass(parentContext.Item, parentContext.ViewType, parentContext)
{
private readonly Slot slot = slot;
private readonly ItemUiContext itemUiContext = itemUiContext;
public override ItemInfoInteractionsAbstractClass<EItemInfoButton> GetItemContextInteractions(Action closeAction)
{
return new EmptySlotMenu(slot, ItemContextAbstractClass, itemUiContext, () =>
{
Dispose();
closeAction?.Invoke();
});
}
public override ItemContextAbstractClass CreateChild(Item item)
{
// Should never happen
throw new NotImplementedException();
}
}
}

View File

@@ -1,6 +1,7 @@
using Comfort.Common;
using EFT.InventoryLogic;
using EFT.UI;
using EFT.UI.DragAndDrop;
using HarmonyLib;
using SPT.Reflection.Patching;
using SPT.Reflection.Utils;
@@ -9,6 +10,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using TMPro;
using UIFixes.ContextMenus;
using UnityEngine;
namespace UIFixes
@@ -81,6 +83,9 @@ namespace UIFixes
new EnableInsureInnerItemsPatch().Enable();
new DisableLoadPresetOnBulletsPatch().Enable();
new EmptySlotMenuPatch().Enable();
new EmptySlotMenuRemovePatch().Enable();
}
public class ContextMenuNamesPatch : ModulePatch
@@ -422,6 +427,44 @@ namespace UIFixes
}
}
public class EmptySlotMenuPatch : ModulePatch
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.DeclaredMethod(typeof(ModSlotView), nameof(ModSlotView.Show));
}
[PatchPostfix]
public static void Postfix(ModSlotView __instance, Slot slot, ItemContextAbstractClass parentItemContext, ItemUiContext itemUiContext)
{
if (!Settings.EnableSlotSearch.Value || slot.ContainedItem != null)
{
return;
}
EmptySlotMenuTrigger menuTrigger = __instance.GetOrAddComponent<EmptySlotMenuTrigger>();
menuTrigger.Init(slot, parentItemContext, itemUiContext);
}
}
public class EmptySlotMenuRemovePatch : ModulePatch
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.DeclaredMethod(typeof(ModSlotView), nameof(ModSlotView.SetupItemView));
}
[PatchPostfix]
public static void Postfix(ModSlotView __instance)
{
EmptySlotMenuTrigger menuTrigger = __instance.GetComponent<EmptySlotMenuTrigger>();
if (menuTrigger != null)
{
UnityEngine.Object.Destroy(menuTrigger);
}
}
}
public class PositionSubMenuPatch : ModulePatch
{
protected override MethodBase GetTargetMethod()

View File

@@ -5,6 +5,7 @@ using HarmonyLib;
using SPT.Reflection.Patching;
using System.Reflection;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
namespace UIFixes
@@ -29,6 +30,8 @@ namespace UIFixes
public class ItemUiContextPatch : ModulePatch
{
private static ItemInfoInteractionsAbstractClass<EItemInfoButton> Interactions;
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.Update));
@@ -51,57 +54,60 @@ namespace UIFixes
return;
}
var interactions = __instance.GetItemContextInteractions(itemContext, null);
if (Settings.InspectKeyBind.Value.IsDown())
{
interactions.ExecuteInteraction(EItemInfoButton.Inspect);
TryInteraction(__instance, itemContext, EItemInfoButton.Inspect);
}
if (Settings.OpenKeyBind.Value.IsDown())
{
interactions.ExecuteInteraction(EItemInfoButton.Open);
TryInteraction(__instance, itemContext, EItemInfoButton.Open);
}
if (Settings.TopUpKeyBind.Value.IsDown())
{
interactions.ExecuteInteraction(EItemInfoButton.TopUp);
TryInteraction(__instance, itemContext, EItemInfoButton.TopUp);
}
if (Settings.UseKeyBind.Value.IsDown())
{
interactions.ExecuteInteraction(EItemInfoButton.Use);
TryInteraction(__instance, itemContext, EItemInfoButton.Use);
}
if (Settings.UseAllKeyBind.Value.IsDown())
{
if (!interactions.ExecuteInteraction(EItemInfoButton.UseAll))
{
interactions.ExecuteInteraction(EItemInfoButton.Use);
}
TryInteraction(__instance, itemContext, EItemInfoButton.UseAll, EItemInfoButton.Use);
}
if (Settings.UnloadKeyBind.Value.IsDown())
{
if (!interactions.ExecuteInteraction(EItemInfoButton.Unload))
{
interactions.ExecuteInteraction(EItemInfoButton.UnloadAmmo);
}
TryInteraction(__instance, itemContext, EItemInfoButton.Unload, EItemInfoButton.UnloadAmmo);
}
if (Settings.UnpackKeyBind.Value.IsDown())
{
interactions.ExecuteInteraction(EItemInfoButton.Unpack);
TryInteraction(__instance, itemContext, EItemInfoButton.Unpack);
}
if (Settings.FilterByKeyBind.Value.IsDown())
{
interactions.ExecuteInteraction(EItemInfoButton.FilterSearch);
TryInteraction(__instance, itemContext, EItemInfoButton.FilterSearch);
}
if (Settings.LinkedSearchKeyBind.Value.IsDown())
{
interactions.ExecuteInteraction(EItemInfoButton.LinkedSearch);
TryInteraction(__instance, itemContext, EItemInfoButton.LinkedSearch);
}
Interactions = null;
}
private static void TryInteraction(ItemUiContext itemUiContext, ItemContextAbstractClass itemContext, EItemInfoButton interaction, EItemInfoButton? fallbackInteraction = null)
{
Interactions ??= itemUiContext.GetItemContextInteractions(itemContext, null);
if (!Interactions.ExecuteInteraction(interaction) && fallbackInteraction.HasValue)
{
Interactions.ExecuteInteraction(fallbackInteraction.Value);
}
}
}

View File

@@ -0,0 +1,55 @@
using EFT.UI.Ragfair;
using HarmonyLib;
using SPT.Reflection.Patching;
using System.Linq;
using System.Reflection;
namespace UIFixes
{
public static class FleaSlotSearchPatches
{
public static void Enable()
{
new HandbookWorkaroundPatch().Enable();
}
public class HandbookWorkaroundPatch : ModulePatch
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(typeof(RagFairClass), nameof(RagFairClass.method_24));
}
[PatchPrefix]
public static void Prefix(GClass3219[] searches, ref string __state, HandbookClass ___handbookClass)
{
if (!Settings.EnableSlotSearch.Value)
{
return;
}
GClass3219 search = searches.FirstOrDefault(s => s.Type == EFilterType.LinkedSearch && s.StringValue.Contains(":"));
if (search != null)
{
__state = search.StringValue.Split(':')[0];
___handbookClass[__state].Data.Id = search.StringValue;
searches[searches.IndexOf(search)] = new GClass3219(EFilterType.LinkedSearch, __state, search.Add);
}
}
[PatchPostfix]
public static void Postfix(ref string __state, HandbookClass ___handbookClass)
{
if (!Settings.EnableSlotSearch.Value)
{
return;
}
if (__state != null)
{
___handbookClass[__state].Data.Id = __state;
}
}
}
}
}

View File

@@ -63,10 +63,7 @@ namespace UIFixes
var sums = R.Money.GetMoneySums(inventoryItems);
NumberFormatInfo numberFormatInfo = new NumberFormatInfo
{
NumberGroupSeparator = " "
};
NumberFormatInfo numberFormatInfo = new() { NumberGroupSeparator = " " };
____roubles.text = sums[ECurrencyType.RUB].ToString("N0", numberFormatInfo);
____euros.text = sums[ECurrencyType.EUR].ToString("N0", numberFormatInfo);
@@ -114,10 +111,7 @@ namespace UIFixes
var sums = R.Money.GetMoneySums(inventoryItems);
NumberFormatInfo numberFormatInfo = new NumberFormatInfo
{
NumberGroupSeparator = " "
};
NumberFormatInfo numberFormatInfo = new() { NumberGroupSeparator = " " };
____roubles.text = GClass2531.GetCurrencyChar(ECurrencyType.RUB) + " " + sums[ECurrencyType.RUB].ToString("N0", numberFormatInfo);
____euros.text = GClass2531.GetCurrencyChar(ECurrencyType.EUR) + " " + sums[ECurrencyType.EUR].ToString("N0", numberFormatInfo);

View File

@@ -61,6 +61,7 @@ namespace UIFixes
ReorderGridsPatches.Enable();
NoRandomGrenadesPatch.Init();
GPCoinPatches.Enable();
FleaSlotSearchPatches.Enable();
}
public static bool InRaid()

View File

@@ -104,6 +104,7 @@ namespace UIFixes
// Flea Market
public static ConfigEntry<bool> EnableFleaHistory { get; set; }
public static ConfigEntry<bool> EnableSlotSearch { get; set; }
public static ConfigEntry<bool> ShowRequiredQuest { get; set; }
public static ConfigEntry<bool> AutoExpandCategories { get; set; }
public static ConfigEntry<bool> KeepAddOfferOpen { get; set; }
@@ -598,6 +599,15 @@ namespace UIFixes
null,
new ConfigurationManagerAttributes { })));
configEntries.Add(EnableSlotSearch = config.Bind(
FleaMarketSection,
"Enable Linked Slot Search",
true,
new ConfigDescription(
"Add a context menu to empty mod slots and allow linked searches for specifically that slot",
null,
new ConfigurationManagerAttributes { })));
configEntries.Add(AutoExpandCategories = config.Bind(
FleaMarketSection,
"Auto-expand Categories",

View File

@@ -0,0 +1,45 @@
import { ItemHelper } from "@spt/helpers/ItemHelper";
import { ITemplateItem } from "@spt/models/eft/common/tables/ITemplateItem";
import { ILogger } from "@spt/models/spt/utils/ILogger";
import { DatabaseService } from "@spt/services/DatabaseService";
import { RagfairLinkedItemService } from "@spt/services/RagfairLinkedItemService";
import { inject, injectable } from "tsyringe";
@injectable()
export class RagfairLinkedSlotItemService extends RagfairLinkedItemService {
constructor(
@inject("DatabaseService") protected databaseService: DatabaseService,
@inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("PrimaryLogger") protected logger: ILogger
) {
super(databaseService, itemHelper);
}
public override getLinkedItems(linkedSearchId: string): Set<string> {
const [tpl, slotName] = linkedSearchId.split(":", 2);
if (slotName) {
this.logger.info(`UIFixes: Finding items for specific slot ${tpl}:${slotName}`);
return this.getSpecificFilter(this.databaseService.getItems()[tpl], slotName);
}
return super.getLinkedItems(tpl);
}
protected getSpecificFilter(item: ITemplateItem, slotName: string): Set<string> {
const results = new Set<string>();
// For whatever reason, all chamber slots have the name "patron_in_weapon"
const groupName = slotName === "patron_in_weapon" ? "Chambers" : "Slots";
const group = item._props[groupName] ?? [];
const sub = group.find(slot => slot._name === slotName);
for (const filter of sub?._props?.filters ?? []) {
for (const f of filter.Filter) {
results.add(f);
}
}
return results;
}
}

View File

@@ -10,6 +10,7 @@ import type { ILogger } from "@spt/models/spt/utils/ILogger";
import type { DatabaseService } from "@spt/services/DatabaseService";
import type { StaticRouterModService } from "@spt/services/mod/staticRouter/StaticRouterModService";
import type { ICloner } from "@spt/utils/cloners/ICloner";
import { RagfairLinkedSlotItemService } from "./RagfairLinkedSlotItemService";
import config from "../config/config.json";
@@ -143,6 +144,10 @@ class UIFixes implements IPreSptLoadMod {
);
}
// Register slot-aware linked item service
container.register<RagfairLinkedSlotItemService>("RagfairLinkedSlotItemService", RagfairLinkedSlotItemService);
container.register("RagfairLinkedItemService", { useToken: "RagfairLinkedSlotItemService" });
staticRouterModService.registerStaticRouter(
"UIFixesRoutes",
[