empty slot linked search
This commit is contained in:
29
ContextMenus/EmptySlotMenu.cs
Normal file
29
ContextMenus/EmptySlotMenu.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
82
ContextMenus/EmptySlotMenuTrigger.cs
Normal file
82
ContextMenus/EmptySlotMenuTrigger.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -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()
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
55
Patches/FleaSlotSearchPatches.cs
Normal file
55
Patches/FleaSlotSearchPatches.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
|
@@ -61,6 +61,7 @@ namespace UIFixes
|
||||
ReorderGridsPatches.Enable();
|
||||
NoRandomGrenadesPatch.Init();
|
||||
GPCoinPatches.Enable();
|
||||
FleaSlotSearchPatches.Enable();
|
||||
}
|
||||
|
||||
public static bool InRaid()
|
||||
|
10
Settings.cs
10
Settings.cs
@@ -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",
|
||||
|
45
server/src/RagfairLinkedSlotItemService.ts
Normal file
45
server/src/RagfairLinkedSlotItemService.ts
Normal 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;
|
||||
}
|
||||
}
|
@@ -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",
|
||||
[
|
||||
|
Reference in New Issue
Block a user