Compare commits
23 Commits
bd9e54fedb
...
d9dda127a7
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d9dda127a7 | ||
![]() |
35d54c282e | ||
![]() |
6cd4cd3ddf | ||
![]() |
89908157f1 | ||
![]() |
cfe32c55cc | ||
![]() |
14c2e79658 | ||
![]() |
7c5e31e677 | ||
![]() |
0a656a2c4b | ||
![]() |
41699a422f | ||
![]() |
30ad64ec40 | ||
![]() |
8046557121 | ||
![]() |
c52435ee37 | ||
![]() |
575e33f04a | ||
![]() |
328eedbc89 | ||
![]() |
0c5d55308c | ||
![]() |
f658724d26 | ||
![]() |
8e63900164 | ||
![]() |
dedce62db5 | ||
![]() |
e431c82ba6 | ||
![]() |
94b080159d | ||
![]() |
e19cf80c35 | ||
![]() |
fea43bcada | ||
![]() |
b748eb96ba |
@@ -1,6 +1,7 @@
|
||||
using EFT.InventoryLogic;
|
||||
using EFT.UI.DragAndDrop;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UIFixes;
|
||||
|
||||
@@ -10,7 +11,7 @@ public static class ExtraItemProperties
|
||||
|
||||
private class Properties
|
||||
{
|
||||
public bool Reordered;
|
||||
public bool Reordered = false;
|
||||
}
|
||||
|
||||
public static bool GetReordered(this Item item) => properties.GetOrCreateValue(item).Reordered;
|
||||
@@ -23,7 +24,7 @@ public static class ExtraTemplatedGridsViewProperties
|
||||
|
||||
private class Properties
|
||||
{
|
||||
public bool Reordered;
|
||||
public bool Reordered = false;
|
||||
}
|
||||
|
||||
public static bool GetReordered(this TemplatedGridsView gridsView) => properties.GetOrCreateValue(gridsView).Reordered;
|
||||
@@ -42,3 +43,47 @@ public static class ExtraTradingGridProperties
|
||||
public static bool GetShowOutOfStock(this TradingGridView gridView) => properties.GetOrCreateValue(gridView).ShowOutOfStock;
|
||||
public static void SetShowOutOfStock(this TradingGridView gridView, bool value) => properties.GetOrCreateValue(gridView).ShowOutOfStock = value;
|
||||
}
|
||||
|
||||
public static class ExtraRagfairOfferItemViewProperties
|
||||
{
|
||||
private static readonly ConditionalWeakTable<RagfairOfferItemView, Properties> properties = new();
|
||||
|
||||
private class Properties
|
||||
{
|
||||
public Vector2? SizeOverride = null;
|
||||
public bool ShowCaption = false;
|
||||
public string Inscription = null;
|
||||
public string Count = null;
|
||||
public string Tooltip = null;
|
||||
}
|
||||
|
||||
public static Vector2? GetSizeOverride(this RagfairOfferItemView itemView) => properties.GetOrCreateValue(itemView).SizeOverride;
|
||||
public static void SetSizeOverride(this RagfairOfferItemView itemView, Vector2 value) => properties.GetOrCreateValue(itemView).SizeOverride = value;
|
||||
|
||||
public static bool GetShowCaption(this RagfairOfferItemView itemView) => properties.GetOrCreateValue(itemView).ShowCaption;
|
||||
public static void SetShowCaption(this RagfairOfferItemView itemView, bool value) => properties.GetOrCreateValue(itemView).ShowCaption = value;
|
||||
|
||||
public static string GetInscription(this RagfairOfferItemView itemView) => properties.GetOrCreateValue(itemView).Inscription;
|
||||
public static void SetInscription(this RagfairOfferItemView itemView, string value) => properties.GetOrCreateValue(itemView).Inscription = value;
|
||||
|
||||
public static string GetCount(this RagfairOfferItemView itemView) => properties.GetOrCreateValue(itemView).Count;
|
||||
public static void SetCount(this RagfairOfferItemView itemView, string value) => properties.GetOrCreateValue(itemView).Count = value;
|
||||
|
||||
public static string GetTooltip(this RagfairOfferItemView itemView) => properties.GetOrCreateValue(itemView).Tooltip;
|
||||
public static void SetTooltip(this RagfairOfferItemView itemView, string value) => properties.GetOrCreateValue(itemView).Tooltip = value;
|
||||
|
||||
}
|
||||
|
||||
public static class ExtraItemViewStatsProperties
|
||||
{
|
||||
private static readonly ConditionalWeakTable<ItemViewStats, Properties> properties = new();
|
||||
|
||||
private class Properties
|
||||
{
|
||||
public bool HideMods = false;
|
||||
}
|
||||
|
||||
public static bool GetHideMods(this ItemViewStats itemViewStats) => properties.GetOrCreateValue(itemViewStats).HideMods;
|
||||
public static void SetHideMods(this ItemViewStats itemViewStats, bool value) => properties.GetOrCreateValue(itemViewStats).HideMods = value;
|
||||
}
|
||||
|
||||
|
@@ -18,6 +18,7 @@ global using DiscardResult = GClass2799;
|
||||
global using ItemSorter = GClass2772;
|
||||
global using ItemWithLocation = GClass2521;
|
||||
global using SearchableGrid = GClass2516;
|
||||
global using CursorManager = GClass3034;
|
||||
|
||||
// State machine states
|
||||
global using FirearmReadyState = EFT.Player.FirearmController.GClass1619;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using Comfort.Common;
|
||||
using BepInEx.Configuration;
|
||||
using Comfort.Common;
|
||||
using EFT.UI;
|
||||
using EFT.UI.DragAndDrop;
|
||||
using System;
|
||||
@@ -24,6 +25,8 @@ public class DrawMultiSelect : MonoBehaviour
|
||||
private bool drawing;
|
||||
private bool secondary;
|
||||
|
||||
private static Vector2 Deadzone = new(5f, 5f);
|
||||
|
||||
public void Start()
|
||||
{
|
||||
selectTexture = new Texture2D(1, 1);
|
||||
@@ -56,37 +59,34 @@ public class DrawMultiSelect : MonoBehaviour
|
||||
return;
|
||||
}
|
||||
|
||||
// checking ItemUiContext is a quick and easy way to know the mouse is over an item
|
||||
if (Input.GetKeyDown(Settings.SelectionBoxKey.Value.MainKey) && ItemUiContext.Instance.R().ItemContext == null)
|
||||
if (Settings.SelectionBoxKey.Value.IsDownIgnoreOthers())
|
||||
{
|
||||
PointerEventData eventData = new(EventSystem.current)
|
||||
bool shiftDown = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
|
||||
|
||||
// Only need to check we aren't over draggables/clickables if the multiselect key is left mouse
|
||||
if (Settings.SelectionBoxKey.Value.MainKey == KeyCode.Mouse0 && !shiftDown && !MouseIsOverClickable())
|
||||
{
|
||||
position = Input.mousePosition
|
||||
};
|
||||
|
||||
List<RaycastResult> results = [];
|
||||
localRaycaster.Raycast(eventData, results);
|
||||
preloaderRaycaster.Raycast(eventData, results);
|
||||
|
||||
foreach (GameObject gameObject in results.Select(r => r.gameObject))
|
||||
{
|
||||
var draggables = gameObject.GetComponents<MonoBehaviour>()
|
||||
.Where(c => c is IDragHandler || c is IBeginDragHandler || c is TextMeshProUGUI) // tmp_inputfield is draggable, but textmesh isn't so explicitly include
|
||||
.Where(c => c is not ScrollRectNoDrag) // this disables scrolling, it doesn't add it
|
||||
.Where(c => c.name != "Inner"); // there's a random DragTrigger sitting in ItemInfoWindows
|
||||
|
||||
var clickables = gameObject.GetComponents<MonoBehaviour>()
|
||||
.Where(c => c is IPointerClickHandler || c is IPointerDownHandler || c is IPointerUpHandler);
|
||||
|
||||
if (draggables.Any() || clickables.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
selectOrigin = Input.mousePosition;
|
||||
drawing = true;
|
||||
secondary = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
|
||||
secondary = shiftDown;
|
||||
|
||||
if (!secondary)
|
||||
{
|
||||
MultiSelect.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (drawing && !Settings.SelectionBoxKey.Value.IsPressedIgnoreOthers())
|
||||
{
|
||||
drawing = false;
|
||||
if (secondary)
|
||||
{
|
||||
MultiSelect.CombineSecondary();
|
||||
secondary = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (drawing)
|
||||
@@ -94,6 +94,10 @@ public class DrawMultiSelect : MonoBehaviour
|
||||
selectEnd = Input.mousePosition;
|
||||
|
||||
Rect selectRect = new(selectOrigin, selectEnd - selectOrigin);
|
||||
if (Mathf.Abs(selectRect.size.x) < Deadzone.x && Mathf.Abs(selectRect.size.y) < Deadzone.y)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If not secondary, then we can kick out any non-rendered items, plus they won't be covered by the foreach below
|
||||
if (!secondary)
|
||||
@@ -136,16 +140,6 @@ public class DrawMultiSelect : MonoBehaviour
|
||||
MultiSelect.Deselect(gridItemView, secondary);
|
||||
}
|
||||
}
|
||||
|
||||
if (drawing && !Input.GetKey(Settings.SelectionBoxKey.Value.MainKey))
|
||||
{
|
||||
drawing = false;
|
||||
if (secondary)
|
||||
{
|
||||
MultiSelect.CombineSecondary();
|
||||
secondary = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
@@ -171,6 +165,42 @@ public class DrawMultiSelect : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
private bool MouseIsOverClickable()
|
||||
{
|
||||
// checking ItemUiContext is a quick and easy way to know the mouse is over an item
|
||||
if (ItemUiContext.Instance.R().ItemContext != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PointerEventData eventData = new(EventSystem.current)
|
||||
{
|
||||
position = Input.mousePosition
|
||||
};
|
||||
|
||||
List<RaycastResult> results = [];
|
||||
localRaycaster.Raycast(eventData, results);
|
||||
preloaderRaycaster.Raycast(eventData, results);
|
||||
|
||||
foreach (GameObject gameObject in results.Select(r => r.gameObject))
|
||||
{
|
||||
var draggables = gameObject.GetComponents<MonoBehaviour>()
|
||||
.Where(c => c is IDragHandler || c is IBeginDragHandler || c is TextMeshProUGUI) // tmp_inputfield is draggable, but textmesh isn't so explicitly include
|
||||
.Where(c => c is not ScrollRectNoDrag) // this disables scrolling, it doesn't add it
|
||||
.Where(c => c.name != "Inner"); // there's a random DragTrigger sitting in ItemInfoWindows
|
||||
|
||||
var clickables = gameObject.GetComponents<MonoBehaviour>()
|
||||
.Where(c => c is IPointerClickHandler || c is IPointerDownHandler || c is IPointerUpHandler);
|
||||
|
||||
if (draggables.Any() || clickables.Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsOnTop(Rect itemRect, Transform itemTransform, GraphicRaycaster raycaster)
|
||||
{
|
||||
// Otherwise, ensure it's not overlapped by window UI
|
||||
|
@@ -125,6 +125,8 @@ public class MultiSelect
|
||||
|
||||
public static void OnKillItemView(GridItemView itemView)
|
||||
{
|
||||
CombineSecondary();
|
||||
|
||||
MultiSelectItemContext itemContext = SelectedItems.FirstOrDefault(x => x.Value == itemView).Key;
|
||||
if (itemContext != null)
|
||||
{
|
||||
@@ -140,6 +142,8 @@ public class MultiSelect
|
||||
return;
|
||||
}
|
||||
|
||||
CombineSecondary();
|
||||
|
||||
MultiSelectItemContext itemContext = SelectedItems.FirstOrDefault(x => x.Key.Item == itemView.Item).Key;
|
||||
if (itemContext != null)
|
||||
{
|
||||
@@ -158,6 +162,8 @@ public class MultiSelect
|
||||
return;
|
||||
}
|
||||
|
||||
CombineSecondary();
|
||||
|
||||
MultiSelectItemContext oldItemContext = SelectedItems.FirstOrDefault(x => x.Key.Item == eventArgs.Item).Key;
|
||||
if (oldItemContext != null)
|
||||
{
|
||||
@@ -221,7 +227,7 @@ public class MultiSelect
|
||||
|
||||
public static bool Active
|
||||
{
|
||||
get { return SelectedItems.Count > 0; }
|
||||
get { return SelectedItems.Count > 0 || SecondaryItems.Count > 0; }
|
||||
}
|
||||
|
||||
// Sort the items to prioritize the items that share a grid with the dragged item, prepend the dragContext as the first one
|
||||
|
@@ -69,6 +69,6 @@ public class MultiSelectDebug : MonoBehaviour
|
||||
LocationInGrid location = address is GridItemAddress gridAddress ? gridAddress.LocationInGrid : null;
|
||||
string locationString = location != null ? $"({location.x}, {location.y})" : "(slot)";
|
||||
|
||||
return $"x{itemContext.Item.StackObjectsCount} {address.Container.ID} {locationString} {itemContext.Item.Name.Localized()}";
|
||||
return $"x{itemContext.Item.StackObjectsCount} {(address != null ? address.Container.ID : "")} {locationString} {itemContext.Item.Name.Localized()}";
|
||||
}
|
||||
}
|
||||
|
334
Patches/BarterOfferPatches.cs
Normal file
334
Patches/BarterOfferPatches.cs
Normal file
@@ -0,0 +1,334 @@
|
||||
using EFT;
|
||||
using EFT.InventoryLogic;
|
||||
using EFT.UI;
|
||||
using EFT.UI.DragAndDrop;
|
||||
using EFT.UI.Ragfair;
|
||||
using HarmonyLib;
|
||||
using SPT.Reflection.Patching;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UIFixes;
|
||||
|
||||
public static class BarterOfferPatches
|
||||
{
|
||||
public static void Enable()
|
||||
{
|
||||
new IconsPatch().Enable();
|
||||
new ItemViewScalePatch().Enable();
|
||||
new ItemUpdateInfoPatch().Enable();
|
||||
new HideItemViewStatsPatch().Enable();
|
||||
new OverrideGridItemViewTooltipPatch().Enable();
|
||||
|
||||
new NoPointerEnterPatch().Enable();
|
||||
new NoPointerExitPatch().Enable();
|
||||
new NoPointerClickPatch().Enable();
|
||||
}
|
||||
|
||||
public class IconsPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.DeclaredMethod(typeof(OfferItemPriceBarter), nameof(OfferItemPriceBarter.Show));
|
||||
}
|
||||
|
||||
[PatchPostfix]
|
||||
public static void Postfix(
|
||||
OfferItemPriceBarter __instance,
|
||||
IExchangeRequirement requirement,
|
||||
ItemTooltip tooltip,
|
||||
Offer offer,
|
||||
InventoryControllerClass inventoryController,
|
||||
ItemUiContext itemUiContext,
|
||||
InsuranceCompanyClass insuranceCompany,
|
||||
int index,
|
||||
bool expanded,
|
||||
GameObject ____barterIcon,
|
||||
TextMeshProUGUI ____requirementName,
|
||||
GameObject ____separator)
|
||||
{
|
||||
if (!Settings.ShowBarterIcons.Value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (requirement is not HandoverRequirement handoverRequirement)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool isDogtag = requirement.Item.GetItemComponent<DogtagComponent>() != null;
|
||||
|
||||
HorizontalOrVerticalLayoutGroup layoutGroup = __instance.transform.parent.GetComponent<HorizontalOrVerticalLayoutGroup>();
|
||||
if (layoutGroup != null)
|
||||
{
|
||||
layoutGroup.spacing = 1f;
|
||||
}
|
||||
|
||||
Vector2 smallSizeDelta = ____barterIcon.RectTransform().sizeDelta;
|
||||
|
||||
RagfairOfferItemView itemView = ItemViewFactory.CreateFromPool<RagfairOfferItemView>("ragfair_offer_layout");
|
||||
itemView.transform.SetParent(__instance.transform, false);
|
||||
if (!expanded)
|
||||
{
|
||||
itemView.SetSizeOverride(smallSizeDelta);
|
||||
|
||||
ItemViewStats itemViewStats = itemView.GetComponent<ItemViewStats>();
|
||||
itemViewStats.SetHideMods(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isDogtag)
|
||||
{
|
||||
if (handoverRequirement.Side != EDogtagExchangeSide.Any)
|
||||
{
|
||||
itemView.SetShowCaption(true);
|
||||
}
|
||||
|
||||
itemView.SetInscription("LVLKILLLIST".Localized() + " " + handoverRequirement.Level);
|
||||
}
|
||||
|
||||
int ownedCount = GetOwnedCount(requirement, inventoryController);
|
||||
itemView.SetCount(string.Format("<color=#{2}><b>{0}</b></color>/{1}", ownedCount.FormatSeparate(" "), requirement.IntCount.FormatSeparate(" "), "C5C3B2"));
|
||||
}
|
||||
|
||||
if (isDogtag)
|
||||
{
|
||||
itemView.SetTooltip(string.Concat(
|
||||
[
|
||||
"Dogtag".Localized(),
|
||||
" ≥ ",
|
||||
handoverRequirement.Level,
|
||||
" ",
|
||||
"LVLKILLLIST".Localized(),
|
||||
(handoverRequirement.Side != EDogtagExchangeSide.Any ? ", " + handoverRequirement.Side : "").ToUpper()
|
||||
]));
|
||||
}
|
||||
|
||||
Vector2 sizeDelta = expanded ? new Vector2(64f, 64f) : smallSizeDelta;
|
||||
LayoutElement layoutElement = itemView.GetComponent<LayoutElement>();
|
||||
layoutElement.preferredWidth = layoutElement.minWidth = sizeDelta.x;
|
||||
layoutElement.preferredHeight = layoutElement.minHeight = sizeDelta.y;
|
||||
|
||||
itemView.Show(null, requirement.Item, ItemRotation.Horizontal, false, inventoryController, requirement.Item.Owner, itemUiContext, null);
|
||||
|
||||
ItemViewManager itemViewManager = __instance.GetOrAddComponent<ItemViewManager>();
|
||||
itemViewManager.Init(itemView);
|
||||
|
||||
____barterIcon.SetActive(false);
|
||||
____separator?.SetActive(false);
|
||||
|
||||
if (expanded)
|
||||
{
|
||||
____requirementName.transform.parent.gameObject.SetActive(false); // The name and the ? icon
|
||||
}
|
||||
else
|
||||
{
|
||||
____requirementName.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetOwnedCount(IExchangeRequirement requirement, InventoryControllerClass inventoryController)
|
||||
{
|
||||
List<Item> allItems = [];
|
||||
inventoryController.Inventory.Stash.GetAllAssembledItemsNonAlloc(allItems);
|
||||
inventoryController.Inventory.QuestStashItems.GetAllAssembledItemsNonAlloc(allItems);
|
||||
inventoryController.Inventory.QuestRaidItems.GetAllAssembledItemsNonAlloc(allItems);
|
||||
|
||||
if (requirement is not HandoverRequirement handoverRequirement)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (requirement.Item.GetItemComponent<DogtagComponent>() != null)
|
||||
{
|
||||
return allItems.Where(item => RagFairClass.CanUseForBarterExchange(item, out string error))
|
||||
.Select(item => item.GetItemComponent<DogtagComponent>())
|
||||
.Where(dogtag => dogtag != null)
|
||||
.Where(dogtag => dogtag.Level >= handoverRequirement.Level)
|
||||
.Where(dogtag => handoverRequirement.Side == EDogtagExchangeSide.Any || dogtag.Side.ToString() == handoverRequirement.Side.ToString())
|
||||
.Count();
|
||||
}
|
||||
|
||||
return allItems.Where(item => RagFairClass.CanUseForBarterExchange(item, out string error))
|
||||
.Where(item => item.TemplateId == requirement.Item.TemplateId)
|
||||
.Where(item => !requirement.OnlyFunctional || item is not LootItemClass lootItem || !lootItem.MissingVitalParts.Any())
|
||||
.Where(item => item is not GInterface325 encodable || requirement.Item is not GInterface325 || encodable.IsEncoded() == requirement.IsEncoded)
|
||||
.Sum(item => item.StackObjectsCount);
|
||||
}
|
||||
}
|
||||
|
||||
public class ItemViewScalePatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.DeclaredMethod(typeof(RagfairOfferItemView), nameof(RagfairOfferItemView.UpdateScale));
|
||||
}
|
||||
|
||||
[PatchPostfix]
|
||||
public static void Postfix(RagfairOfferItemView __instance, Image ___MainImage)
|
||||
{
|
||||
Vector2? sizeOverride = __instance.GetSizeOverride();
|
||||
if (sizeOverride.HasValue)
|
||||
{
|
||||
Vector2 sizeDelta = ___MainImage.rectTransform.sizeDelta;
|
||||
float x = sizeDelta.x;
|
||||
float y = sizeDelta.y;
|
||||
|
||||
// Calculate scale and multiply to preserve aspect ratio
|
||||
float scale = Mathf.Min((float)sizeOverride.Value.x / x, (float)sizeOverride.Value.y / y);
|
||||
___MainImage.rectTransform.sizeDelta = new Vector2(x * scale, y * scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ItemUpdateInfoPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.DeclaredMethod(typeof(RagfairOfferItemView), nameof(RagfairOfferItemView.UpdateInfo));
|
||||
}
|
||||
|
||||
[PatchPostfix]
|
||||
public static void Postfix(RagfairOfferItemView __instance, TextMeshProUGUI ___Caption, TextMeshProUGUI ___ItemInscription, TextMeshProUGUI ___ItemValue)
|
||||
{
|
||||
if (__instance.GetShowCaption())
|
||||
{
|
||||
___Caption.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
string inscription = __instance.GetInscription();
|
||||
if (!string.IsNullOrEmpty(inscription))
|
||||
{
|
||||
___ItemInscription.text = inscription;
|
||||
___ItemInscription.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
string value = __instance.GetCount();
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
___ItemValue.text = value;
|
||||
___ItemValue.fontSize = 16f;
|
||||
___ItemValue.alignment = TextAlignmentOptions.Left;
|
||||
|
||||
RectTransform rectTransform = ___ItemValue.RectTransform();
|
||||
rectTransform.pivot = new Vector2(0f, 0.5f);
|
||||
rectTransform.anchorMin = rectTransform.anchorMax = new Vector2(1f, 0.5f);
|
||||
rectTransform.anchoredPosition = new Vector2(5f, 0f);
|
||||
___ItemValue.gameObject.SetActive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class OverrideGridItemViewTooltipPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.DeclaredMethod(typeof(GridItemView), nameof(GridItemView.ShowTooltip));
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static bool Prefix(GridItemView __instance, ItemUiContext ___ItemUiContext)
|
||||
{
|
||||
if (__instance is not RagfairOfferItemView ragfairOfferItemView)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
string tooltip = ragfairOfferItemView.GetTooltip();
|
||||
if (!string.IsNullOrEmpty(tooltip))
|
||||
{
|
||||
___ItemUiContext.Tooltip.Show(tooltip, null, 0.5f);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class HideItemViewStatsPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(ItemViewStats), nameof(ItemViewStats.SetStaticInfo));
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static bool Prefix(ItemViewStats __instance, Image ____modIcon, Image ____modTypeIcon, Image ____specialIcon, Image ____armorClassIcon)
|
||||
{
|
||||
if (!__instance.GetHideMods())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
____modIcon.gameObject.SetActive(false);
|
||||
____modTypeIcon.gameObject.SetActive(false);
|
||||
____specialIcon.gameObject.SetActive(false);
|
||||
____armorClassIcon?.gameObject.SetActive(false);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class NoPointerEnterPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(OfferItemPriceBarter), nameof(OfferItemPriceBarter.OnPointerEnter));
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static bool Prefix()
|
||||
{
|
||||
return !Settings.ShowBarterIcons.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public class NoPointerExitPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(OfferItemPriceBarter), nameof(OfferItemPriceBarter.OnPointerExit));
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static bool Prefix()
|
||||
{
|
||||
return !Settings.ShowBarterIcons.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public class NoPointerClickPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(OfferItemPriceBarter), nameof(OfferItemPriceBarter.OnPointerClick));
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static bool Prefix()
|
||||
{
|
||||
return !Settings.ShowBarterIcons.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public class ItemViewManager : MonoBehaviour
|
||||
{
|
||||
RagfairOfferItemView itemView;
|
||||
|
||||
public void Init(RagfairOfferItemView itemView)
|
||||
{
|
||||
this.itemView = itemView;
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
itemView.IsStub = true;
|
||||
itemView.Kill();
|
||||
}
|
||||
}
|
||||
}
|
@@ -192,13 +192,20 @@ public static class ContextMenuPatches
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(R.TradingInteractions.Type, "get_SubInteractions");
|
||||
return AccessTools.PropertyGetter(
|
||||
typeof(ItemInfoInteractionsAbstractClass<EItemInfoButton>),
|
||||
nameof(ItemInfoInteractionsAbstractClass<EItemInfoButton>.SubInteractions));
|
||||
}
|
||||
|
||||
[PatchPostfix]
|
||||
public static void Postfix(ref IEnumerable<EItemInfoButton> __result)
|
||||
public static void Postfix(
|
||||
ItemInfoInteractionsAbstractClass<EItemInfoButton> __instance,
|
||||
ref IEnumerable<EItemInfoButton> __result)
|
||||
{
|
||||
__result = __result.Append(EItemInfoButton.Repair).Append(EItemInfoButton.Insure);
|
||||
if (R.TradingInteractions.Type.IsInstanceOfType(__instance))
|
||||
{
|
||||
__result = __result.Append(EItemInfoButton.Repair).Append(EItemInfoButton.Insure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,12 +215,23 @@ public static class ContextMenuPatches
|
||||
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(R.TradingInteractions.Type, "CreateSubInteractions");
|
||||
return AccessTools.Method(
|
||||
typeof(ItemInfoInteractionsAbstractClass<EItemInfoButton>),
|
||||
nameof(ItemInfoInteractionsAbstractClass<EItemInfoButton>.CreateSubInteractions));
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static bool Prefix(object __instance, EItemInfoButton parentInteraction, ISubInteractions subInteractionsWrapper, ItemUiContext ___itemUiContext_0)
|
||||
public static bool Prefix(
|
||||
ItemInfoInteractionsAbstractClass<EItemInfoButton> __instance,
|
||||
EItemInfoButton parentInteraction,
|
||||
ISubInteractions subInteractionsWrapper,
|
||||
ItemUiContext ___itemUiContext_0)
|
||||
{
|
||||
if (!R.TradingInteractions.Type.IsInstanceOfType(__instance))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Clear this, since something else should be active (even a different mouseover of the insurance button)
|
||||
LoadingInsuranceActions = false;
|
||||
|
||||
|
@@ -1,11 +1,11 @@
|
||||
using EFT.InventoryLogic;
|
||||
using Comfort.Common;
|
||||
using EFT.InventoryLogic;
|
||||
using EFT.UI;
|
||||
using EFT.UI.DragAndDrop;
|
||||
using HarmonyLib;
|
||||
using SPT.Reflection.Patching;
|
||||
using System.Reflection;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UIFixes;
|
||||
@@ -19,7 +19,6 @@ public static class ContextMenuShortcutPatches
|
||||
new ItemUiContextPatch().Enable();
|
||||
|
||||
new HideoutItemViewRegisterContextPatch().Enable();
|
||||
new HideoutItemViewUnegisterContextPatch().Enable();
|
||||
|
||||
new TradingPanelRegisterContextPatch().Enable();
|
||||
new TradingPanelUnregisterContextPatch().Enable();
|
||||
@@ -76,12 +75,12 @@ public static class ContextMenuShortcutPatches
|
||||
|
||||
if (Settings.UseAllKeyBind.Value.IsDown())
|
||||
{
|
||||
TryInteraction(__instance, itemContext, EItemInfoButton.UseAll, EItemInfoButton.Use);
|
||||
TryInteraction(__instance, itemContext, EItemInfoButton.UseAll, [EItemInfoButton.Use]);
|
||||
}
|
||||
|
||||
if (Settings.UnloadKeyBind.Value.IsDown())
|
||||
{
|
||||
TryInteraction(__instance, itemContext, EItemInfoButton.Unload, EItemInfoButton.UnloadAmmo);
|
||||
TryInteraction(__instance, itemContext, EItemInfoButton.Unload, [EItemInfoButton.UnloadAmmo]);
|
||||
}
|
||||
|
||||
if (Settings.UnpackKeyBind.Value.IsDown())
|
||||
@@ -99,15 +98,62 @@ public static class ContextMenuShortcutPatches
|
||||
TryInteraction(__instance, itemContext, EItemInfoButton.LinkedSearch);
|
||||
}
|
||||
|
||||
if (Settings.SortingTableKeyBind.Value.IsDown())
|
||||
{
|
||||
MoveToFromSortingTable(itemContext, __instance);
|
||||
}
|
||||
|
||||
if (Settings.ExamineKeyBind.Value.IsDown())
|
||||
{
|
||||
TryInteraction(__instance, itemContext, EItemInfoButton.Examine,
|
||||
[EItemInfoButton.Fold, EItemInfoButton.Unfold, EItemInfoButton.TurnOn, EItemInfoButton.TurnOff, EItemInfoButton.CheckMagazine]);
|
||||
}
|
||||
|
||||
Interactions = null;
|
||||
}
|
||||
|
||||
private static void TryInteraction(ItemUiContext itemUiContext, ItemContextAbstractClass itemContext, EItemInfoButton interaction, EItemInfoButton? fallbackInteraction = null)
|
||||
private static void TryInteraction(ItemUiContext itemUiContext, ItemContextAbstractClass itemContext, EItemInfoButton interaction, EItemInfoButton[] fallbackInteractions = null)
|
||||
{
|
||||
Interactions ??= itemUiContext.GetItemContextInteractions(itemContext, null);
|
||||
if (!Interactions.ExecuteInteraction(interaction) && fallbackInteraction.HasValue)
|
||||
if (!Interactions.ExecuteInteraction(interaction) && fallbackInteractions != null)
|
||||
{
|
||||
Interactions.ExecuteInteraction(fallbackInteraction.Value);
|
||||
foreach (var fallbackInteraction in fallbackInteractions)
|
||||
{
|
||||
if (Interactions.ExecuteInteraction(fallbackInteraction))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void MoveToFromSortingTable(ItemContextAbstractClass itemContext, ItemUiContext itemUiContext)
|
||||
{
|
||||
Item item = itemContext.Item;
|
||||
if (item.Owner is not InventoryControllerClass controller)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SortingTableClass sortingTable = controller.Inventory.SortingTable;
|
||||
bool isInSortingTable = sortingTable != null && item.Parent.Container.ParentItem == sortingTable;
|
||||
|
||||
var operation = isInSortingTable ? itemUiContext.QuickFindAppropriatePlace(itemContext, controller, false, true, true) : itemUiContext.QuickMoveToSortingTable(item, true);
|
||||
if (operation.Succeeded && controller.CanExecute(operation.Value))
|
||||
{
|
||||
if (operation.Value is IDestroyResult destroyResult && destroyResult.ItemsDestroyRequired)
|
||||
{
|
||||
NotificationManagerClass.DisplayWarningNotification(new DestroyError(item, destroyResult.ItemsToDestroy).GetLocalizedDescription());
|
||||
return;
|
||||
}
|
||||
|
||||
controller.RunNetworkTransaction(operation.Value, null);
|
||||
if (itemUiContext.Tooltip != null)
|
||||
{
|
||||
itemUiContext.Tooltip.Close();
|
||||
}
|
||||
|
||||
Singleton<GUISounds>.Instance.PlayItemSound(item.ItemSound, EInventorySoundType.pickup, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,20 +173,6 @@ public static class ContextMenuShortcutPatches
|
||||
}
|
||||
}
|
||||
|
||||
public class HideoutItemViewUnegisterContextPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(HideoutItemView), nameof(HideoutItemView.OnPointerExit));
|
||||
}
|
||||
|
||||
[PatchPostfix]
|
||||
public static void Postfix(HideoutItemView __instance, ItemUiContext ___ItemUiContext)
|
||||
{
|
||||
___ItemUiContext.UnregisterCurrentItemContext(__instance.ItemContext);
|
||||
}
|
||||
}
|
||||
|
||||
public class TradingPanelRegisterContextPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
|
@@ -1,6 +1,8 @@
|
||||
using EFT.UI.Ragfair;
|
||||
using EFT.UI;
|
||||
using EFT.UI.Ragfair;
|
||||
using HarmonyLib;
|
||||
using SPT.Reflection.Patching;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
@@ -12,12 +14,15 @@ public static class FixFleaPatches
|
||||
{
|
||||
public static void Enable()
|
||||
{
|
||||
// These two are anal AF
|
||||
// These are anal AF
|
||||
new DoNotToggleOnMouseOverPatch().Enable();
|
||||
new ToggleOnOpenPatch().Enable();
|
||||
new DropdownHeightPatch().Enable();
|
||||
|
||||
new OfferItemFixMaskPatch().Enable();
|
||||
new OfferViewTweaksPatch().Enable();
|
||||
|
||||
new SearchPatch().Enable();
|
||||
}
|
||||
|
||||
public class DoNotToggleOnMouseOverPatch : ModulePatch
|
||||
@@ -95,4 +100,57 @@ public static class FixFleaPatches
|
||||
timeLeft.childControlWidth = false;
|
||||
}
|
||||
}
|
||||
|
||||
public class SearchPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(RagfairCategoriesPanel), nameof(RagfairCategoriesPanel.method_9));
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static bool Prefix(RagfairCategoriesPanel __instance, string arg)
|
||||
{
|
||||
if (!Settings.ClearFiltersOnSearch.Value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (arg.StartsWith("#") || __instance.Ragfair == null || __instance.EViewListType_0 != EViewListType.AllOffers)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (__instance.FilteredNodes.Values.Sum(node => node.Count) > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__instance.Ragfair.CancellableFilters.Clear();
|
||||
|
||||
FilterRule filterRule = __instance.Ragfair.method_3(EViewListType.AllOffers);
|
||||
filterRule.HandbookId = string.Empty;
|
||||
|
||||
__instance.Ragfair.AddSearchesInRule(filterRule, true);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class DropdownHeightPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.DeclaredMethod(typeof(DropDownBox), nameof(DropDownBox.Init));
|
||||
}
|
||||
|
||||
[PatchPostfix]
|
||||
public static void Postfix(ref float ____maxVisibleHeight)
|
||||
{
|
||||
if (____maxVisibleHeight == 120f)
|
||||
{
|
||||
____maxVisibleHeight = 240f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -26,7 +26,7 @@ public static class MultiSelectPatches
|
||||
// Used to prevent infinite recursion of CanAccept/AcceptItem
|
||||
private static bool InPatch = false;
|
||||
|
||||
// If the CanAccept method should render highlights
|
||||
// Keep track of preview images when dragging
|
||||
private static readonly List<Image> Previews = [];
|
||||
|
||||
// Point that various QuickFindPlace overrides should start at
|
||||
@@ -150,7 +150,7 @@ public static class MultiSelectPatches
|
||||
}
|
||||
|
||||
[PatchPostfix]
|
||||
public static void Postfix(ItemView __instance, PointerEventData eventData)
|
||||
public static void Postfix(ItemView __instance, PointerEventData eventData, TraderControllerClass ___ItemController)
|
||||
{
|
||||
if (!MultiSelect.Enabled || __instance is RagfairNewOfferItemView || __instance is InsuranceItemView)
|
||||
{
|
||||
@@ -161,12 +161,36 @@ public static class MultiSelectPatches
|
||||
bool shiftDown = Input.GetKey(KeyCode.LeftShift) && !Input.GetKey(KeyCode.RightShift);
|
||||
bool altDown = Input.GetKey(KeyCode.LeftAlt) && !Input.GetKey(KeyCode.RightAlt);
|
||||
|
||||
if (Settings.EnableMultiClick.Value && __instance is GridItemView gridItemView && eventData.button == PointerEventData.InputButton.Left && shiftDown && !ctrlDown && !altDown)
|
||||
// If sorting table is open and default shift-click behavior is enabled, don't multiselect
|
||||
bool couldBeSortingTableMove = false;
|
||||
if (Settings.DefaultSortingTableBind.Value &&
|
||||
shiftDown &&
|
||||
eventData.button == PointerEventData.InputButton.Left &&
|
||||
___ItemController is InventoryControllerClass inventoryController)
|
||||
{
|
||||
SortingTableClass sortingTable = inventoryController.Inventory.SortingTable;
|
||||
if (sortingTable != null && sortingTable.IsVisible)
|
||||
{
|
||||
couldBeSortingTableMove = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.EnableMultiClick.Value &&
|
||||
!couldBeSortingTableMove &&
|
||||
__instance is GridItemView gridItemView &&
|
||||
eventData.button == PointerEventData.InputButton.Left &&
|
||||
shiftDown && !ctrlDown && !altDown)
|
||||
{
|
||||
MultiSelect.Toggle(gridItemView);
|
||||
return;
|
||||
}
|
||||
|
||||
// Mainly this tests for when selection box is rebound to another mouse button, to enable secondary selection
|
||||
if (!couldBeSortingTableMove && shiftDown && Settings.SelectionBoxKey.Value.IsDownIgnoreOthers())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (__instance is not GridItemView gridItemView2 || !MultiSelect.IsSelected(gridItemView2))
|
||||
{
|
||||
MultiSelect.Clear();
|
||||
@@ -211,9 +235,14 @@ public static class MultiSelectPatches
|
||||
return false;
|
||||
}
|
||||
|
||||
if (shiftDown)
|
||||
if (shiftDown && !ctrlDown && !altDown)
|
||||
{
|
||||
// Nothing to do, mousedown handled it.
|
||||
if (Settings.DefaultSortingTableBind.Value)
|
||||
{
|
||||
QuickMove(__instance, ___ItemUiContext, ___ItemController, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -222,15 +251,17 @@ public static class MultiSelectPatches
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void QuickMove(GridItemView gridItemView, ItemUiContext itemUiContext, TraderControllerClass itemController)
|
||||
private static void QuickMove(GridItemView gridItemView, ItemUiContext itemUiContext, TraderControllerClass itemController, bool moveToSortingTable = false)
|
||||
{
|
||||
bool succeeded = true;
|
||||
DisableMerge = true;
|
||||
IgnoreItemParent = true;
|
||||
Stack<ItemOperation> operations = new();
|
||||
foreach (DragItemContext selectedItemContext in MultiSelect.SortedItemContexts())
|
||||
foreach (var selectedItemContext in MultiSelect.SortedItemContexts())
|
||||
{
|
||||
ItemOperation operation = itemUiContext.QuickFindAppropriatePlace(selectedItemContext, itemController, false /*forceStash*/, false /*showWarnings*/, false /*simulate*/);
|
||||
ItemOperation operation = moveToSortingTable ?
|
||||
itemUiContext.QuickMoveToSortingTable(selectedItemContext.Item, false /*simulate*/) :
|
||||
itemUiContext.QuickFindAppropriatePlace(selectedItemContext, itemController, false /*forceStash*/, false /*showWarnings*/, false /*simulate*/);
|
||||
if (operation.Succeeded && itemController.CanExecute(operation.Value))
|
||||
{
|
||||
operations.Push(operation);
|
||||
@@ -434,6 +465,13 @@ public static class MultiSelectPatches
|
||||
return AccessTools.Method(typeof(ItemView), nameof(ItemView.OnBeginDrag));
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static bool Prefix()
|
||||
{
|
||||
// Disable drag if shift is down
|
||||
return !Input.GetKey(KeyCode.LeftShift) && !Input.GetKey(KeyCode.RightShift);
|
||||
}
|
||||
|
||||
[PatchPostfix]
|
||||
public static void Postfix(ItemView __instance)
|
||||
{
|
||||
|
@@ -1,59 +0,0 @@
|
||||
using Comfort.Common;
|
||||
using EFT.UI;
|
||||
using HarmonyLib;
|
||||
using SPT.Reflection.Patching;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UIFixes;
|
||||
|
||||
public class OpenSortingTablePatch : ModulePatch
|
||||
{
|
||||
private static readonly EItemUiContextType[] AllowedScreens = [EItemUiContextType.InventoryScreen, EItemUiContextType.ScavengerInventoryScreen];
|
||||
|
||||
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.QuickMoveToSortingTable));
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static bool Prefix(ItemUiContext __instance, ref ItemOperation __result)
|
||||
{
|
||||
// BSG checks visibility, not in-raid. There's a bug where somehow that visibility can be true in raid
|
||||
if (Plugin.InRaid())
|
||||
{
|
||||
__result = new GClass3370("SortingTable/VisibilityError");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Settings.AutoOpenSortingTable.Value || !AllowedScreens.Contains(__instance.ContextType))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Temporary work-around for LootValue bug - bail out if the ALT key is down
|
||||
if (Input.GetKey(KeyCode.LeftAlt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
SortingTableClass sortingTable = __instance.R().InventoryController.Inventory.SortingTable;
|
||||
if (sortingTable != null && !sortingTable.IsVisible)
|
||||
{
|
||||
if (__instance.ContextType == EItemUiContextType.InventoryScreen)
|
||||
{
|
||||
Singleton<CommonUI>.Instance.InventoryScreen.method_6();
|
||||
Singleton<CommonUI>.Instance.InventoryScreen.R().SimpleStashPanel.ChangeSortingTableTabState(true);
|
||||
}
|
||||
else if (__instance.ContextType == EItemUiContextType.ScavengerInventoryScreen)
|
||||
{
|
||||
Singleton<CommonUI>.Instance.ScavengerInventoryScreen.method_7();
|
||||
Singleton<CommonUI>.Instance.ScavengerInventoryScreen.R().SimpleStashPanel.ChangeSortingTableTabState(true);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
98
Patches/OpenSortingTablePatches.cs
Normal file
98
Patches/OpenSortingTablePatches.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using Comfort.Common;
|
||||
using EFT.UI;
|
||||
using EFT.UI.DragAndDrop;
|
||||
using HarmonyLib;
|
||||
using SPT.Reflection.Patching;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
namespace UIFixes;
|
||||
|
||||
public static class OpenSortingTablePatches
|
||||
{
|
||||
public static void Enable()
|
||||
{
|
||||
new AutoOpenPatch().Enable();
|
||||
new DefaultBindPatch().Enable();
|
||||
}
|
||||
|
||||
public class AutoOpenPatch : ModulePatch
|
||||
{
|
||||
private static readonly EItemUiContextType[] AllowedScreens = [EItemUiContextType.InventoryScreen, EItemUiContextType.ScavengerInventoryScreen];
|
||||
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.QuickMoveToSortingTable));
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static bool Prefix(ItemUiContext __instance, ref ItemOperation __result)
|
||||
{
|
||||
// BSG checks visibility, not in-raid. There's a bug where somehow that visibility can be true in raid
|
||||
if (Plugin.InRaid())
|
||||
{
|
||||
__result = new GClass3370("SortingTable/VisibilityError");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allowed screens only, and auto-open is enabled or the custom bind is active
|
||||
if (!AllowedScreens.Contains(__instance.ContextType) || (!Settings.AutoOpenSortingTable.Value && !Settings.SortingTableKeyBind.Value.IsDown()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Temporary work-around for LootValue bug - bail out if the ALT key is down
|
||||
if (Input.GetKey(KeyCode.LeftAlt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
SortingTableClass sortingTable = __instance.R().InventoryController.Inventory.SortingTable;
|
||||
if (sortingTable != null && !sortingTable.IsVisible)
|
||||
{
|
||||
if (__instance.ContextType == EItemUiContextType.InventoryScreen)
|
||||
{
|
||||
Singleton<CommonUI>.Instance.InventoryScreen.method_6();
|
||||
Singleton<CommonUI>.Instance.InventoryScreen.R().SimpleStashPanel.ChangeSortingTableTabState(true);
|
||||
}
|
||||
else if (__instance.ContextType == EItemUiContextType.ScavengerInventoryScreen)
|
||||
{
|
||||
Singleton<CommonUI>.Instance.ScavengerInventoryScreen.method_7();
|
||||
Singleton<CommonUI>.Instance.ScavengerInventoryScreen.R().SimpleStashPanel.ChangeSortingTableTabState(true);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class DefaultBindPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(ItemView), nameof(ItemView.OnClick));
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static bool Prefix(PointerEventData.InputButton button, bool doubleClick)
|
||||
{
|
||||
if (Settings.DefaultSortingTableBind.Value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ctrlDown = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
|
||||
bool altDown = Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt);
|
||||
bool shiftDown = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
|
||||
|
||||
if (button == PointerEventData.InputButton.Left && !doubleClick && !ctrlDown && !altDown && shiftDown)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
156
Patches/ReloadInPlacePatches.cs
Normal file
156
Patches/ReloadInPlacePatches.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using EFT;
|
||||
using EFT.UI;
|
||||
using HarmonyLib;
|
||||
using SPT.Reflection.Patching;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace UIFixes;
|
||||
|
||||
public static class ReloadInPlacePatches
|
||||
{
|
||||
private static bool IsReloading = false;
|
||||
private static MagazineClass FoundMagazine = null;
|
||||
|
||||
public static void Enable()
|
||||
{
|
||||
// These patch ItemUiContext.ReloadWeapon, which is called from the context menu Reload
|
||||
new ReloadInPlacePatch().Enable();
|
||||
new ReloadInPlaceFindMagPatch().Enable();
|
||||
new ReloadInPlaceFindSpotPatch().Enable();
|
||||
|
||||
// This patches the firearmsController code when you hit R in raid with an external magazine class
|
||||
new SwapIfNoSpacePatch().Enable();
|
||||
}
|
||||
|
||||
public class ReloadInPlacePatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.ReloadWeapon));
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static void Prefix()
|
||||
{
|
||||
IsReloading = Settings.SwapMags.Value;
|
||||
}
|
||||
|
||||
[PatchPostfix]
|
||||
public static void Postfix()
|
||||
{
|
||||
IsReloading = false;
|
||||
FoundMagazine = null;
|
||||
}
|
||||
}
|
||||
|
||||
public class ReloadInPlaceFindMagPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.method_5));
|
||||
}
|
||||
|
||||
[PatchPostfix]
|
||||
public static void Postfix(MagazineClass __result)
|
||||
{
|
||||
if (IsReloading)
|
||||
{
|
||||
FoundMagazine = __result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ReloadInPlaceFindSpotPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
Type type = typeof(ItemUiContext).GetNestedTypes().Single(t => t.GetField("currentMagazine") != null);
|
||||
return AccessTools.Method(type, "method_0");
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static void Prefix(StashGridClass grid, ref GStruct414<GClass2801> __state)
|
||||
{
|
||||
if (!Settings.SwapMags.Value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (grid.Contains(FoundMagazine))
|
||||
{
|
||||
__state = InteractionsHandlerClass.Remove(FoundMagazine, grid.ParentItem.Owner as TraderControllerClass, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
[PatchPostfix]
|
||||
public static void Postfix(GStruct414<GClass2801> __state)
|
||||
{
|
||||
if (!Settings.SwapMags.Value || __state.Value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (__state.Succeeded)
|
||||
{
|
||||
__state.Value.RollBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SwapIfNoSpacePatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(Player.FirearmController), nameof(Player.FirearmController.ReloadMag));
|
||||
}
|
||||
|
||||
// By default this method will do a series of removes and adds, but not swap, to reload
|
||||
// This tied to a different animation state machine sequence than Swap(), and is faster than Swap.
|
||||
// So only use Swap if *needed*, otherwise its penalizing all reload speeds
|
||||
[PatchPrefix]
|
||||
public static bool Prefix(Player.FirearmController __instance, MagazineClass magazine, ItemAddressClass gridItemAddress)
|
||||
{
|
||||
// If gridItemAddress isn't null, it already found a place for the current mag, so let it run (unless always swap is enabled)
|
||||
if (!Settings.SwapMags.Value || (gridItemAddress != null && !Settings.AlwaysSwapMags.Value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Weapon doesn't currently have a magazine, let the default run (will load one)
|
||||
MagazineClass currentMagazine = __instance.Weapon.GetCurrentMagazine();
|
||||
if (currentMagazine == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
InventoryControllerClass controller = __instance.Weapon.Owner as InventoryControllerClass;
|
||||
|
||||
// Null address means it couldn't find a spot. Try to remove magazine (temporarily) and try again
|
||||
var operation = InteractionsHandlerClass.Remove(magazine, controller, false, false);
|
||||
if (operation.Failed)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
gridItemAddress = controller.Inventory.Equipment.GetPrioritizedGridsForUnloadedObject(false)
|
||||
.Select(grid => grid.FindLocationForItem(currentMagazine))
|
||||
.Where(address => address != null)
|
||||
.OrderBy(address => address.Grid.GridWidth.Value * address.Grid.GridHeight.Value)
|
||||
.FirstOrDefault(); // BSG's version checks null again, but there's no nulls already. If there's no matches, the enumerable is empty
|
||||
|
||||
// Put the magazine back
|
||||
operation.Value.RollBack();
|
||||
|
||||
if (gridItemAddress == null)
|
||||
{
|
||||
// Didn't work, nowhere to put magazine. Let it run (will drop mag on ground)
|
||||
return true;
|
||||
}
|
||||
|
||||
controller.TryRunNetworkTransaction(InteractionsHandlerClass.Swap(currentMagazine, gridItemAddress, magazine, new GClass2783(__instance.Weapon.GetMagazineSlot()), controller, true), null);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -23,7 +23,10 @@ public static class ScrollPatches
|
||||
new EnchanceTraderStashScrollingPatch().Enable();
|
||||
new EnhanceFleaScrollingPatch().Enable();
|
||||
new EnhanceMailScrollingPatch().Enable();
|
||||
|
||||
new MouseScrollingSpeedPatch().Enable();
|
||||
new LightScrollerSpeedPatch().Enable();
|
||||
|
||||
new EnhanceHideoutScrollingPatch().Enable();
|
||||
new EnhanceTaskListScrollingPatch().Enable();
|
||||
new OpenLastTaskPatch().Enable();
|
||||
@@ -305,6 +308,21 @@ public static class ScrollPatches
|
||||
}
|
||||
}
|
||||
|
||||
public class LightScrollerSpeedPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(LightScroller), nameof(LightScroller.method_1));
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static void Prefix(ref float deltaPixels)
|
||||
{
|
||||
int multi = Settings.UseRaidMouseScrollMulti.Value && Plugin.InRaid() ? Settings.MouseScrollMultiInRaid.Value : Settings.MouseScrollMulti.Value;
|
||||
deltaPixels *= multi;
|
||||
}
|
||||
}
|
||||
|
||||
public class EnhanceTaskListScrollingPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
|
@@ -119,7 +119,6 @@ public static class SortPatches
|
||||
{
|
||||
Error error = null;
|
||||
var mergeableItems = lootItem.Grids.SelectMany(g => g.Items)
|
||||
.OfType<Stackable>()
|
||||
.Where(i => i.StackObjectsCount < i.StackMaxSize)
|
||||
.ToArray();
|
||||
|
||||
@@ -131,7 +130,7 @@ public static class SortPatches
|
||||
continue;
|
||||
}
|
||||
|
||||
if (InteractionsHandlerClass.smethod_0(lootItem.Grids, item, out Stackable targetItem, 1))
|
||||
if (Sorter.FindStackForMerge(lootItem.Grids, item, out Item targetItem, 1))
|
||||
{
|
||||
var operation = InteractionsHandlerClass.TransferOrMerge(item, targetItem, inventoryController, true);
|
||||
if (operation.Succeeded)
|
||||
|
@@ -27,44 +27,10 @@ public static class StackFirItemsPatches
|
||||
return method;
|
||||
}
|
||||
|
||||
// Reimplementing this entire method to ignore SpawnedInSession for certain types
|
||||
[PatchPrefix]
|
||||
public static bool Prefix(IEnumerable<EFT.InventoryLogic.IContainer> containersToPut, Item itemToMerge, ref object mergeableItem, int overrideCount, ref bool __result)
|
||||
public static bool Prefix(IEnumerable<EFT.InventoryLogic.IContainer> containersToPut, Item itemToMerge, ref Item mergeableItem, int overrideCount, ref bool __result)
|
||||
{
|
||||
if (!MergeableItemType.IsInstanceOfType(itemToMerge))
|
||||
{
|
||||
mergeableItem = null;
|
||||
__result = false;
|
||||
}
|
||||
|
||||
if (overrideCount <= 0)
|
||||
{
|
||||
overrideCount = itemToMerge.StackObjectsCount;
|
||||
}
|
||||
|
||||
bool ignoreSpawnedInSession;
|
||||
if (itemToMerge.Template is MoneyClass)
|
||||
{
|
||||
ignoreSpawnedInSession = Settings.MergeFIRMoney.Value;
|
||||
}
|
||||
else if (itemToMerge.Template is AmmoTemplate)
|
||||
{
|
||||
ignoreSpawnedInSession = Settings.MergeFIRAmmo.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
ignoreSpawnedInSession = Settings.MergeFIROther.Value;
|
||||
}
|
||||
|
||||
mergeableItem = containersToPut.SelectMany(x => x.Items)
|
||||
.Where(MergeableItemType.IsInstanceOfType)
|
||||
.Where(x => x != itemToMerge)
|
||||
.Where(x => x.TemplateId == itemToMerge.TemplateId)
|
||||
.Where(x => ignoreSpawnedInSession || x.SpawnedInSession == itemToMerge.SpawnedInSession)
|
||||
.Where(x => x.StackObjectsCount < x.StackMaxSize)
|
||||
.FirstOrDefault(x => overrideCount <= x.StackMaxSize - x.StackObjectsCount);
|
||||
|
||||
__result = mergeableItem != null;
|
||||
__result = Sorter.FindStackForMerge(containersToPut, itemToMerge, out mergeableItem, overrideCount);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -79,19 +45,12 @@ public static class StackFirItemsPatches
|
||||
[PatchPrefix]
|
||||
public static bool Prefix(Item __instance, Item other, ref bool __result)
|
||||
{
|
||||
bool ignoreSpawnedInSession;
|
||||
if (__instance.Template is MoneyClass)
|
||||
bool ignoreSpawnedInSession = __instance.Template switch
|
||||
{
|
||||
ignoreSpawnedInSession = Settings.MergeFIRMoney.Value;
|
||||
}
|
||||
else if (__instance.Template is AmmoTemplate)
|
||||
{
|
||||
ignoreSpawnedInSession = Settings.MergeFIRAmmo.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
ignoreSpawnedInSession = Settings.MergeFIROther.Value;
|
||||
}
|
||||
MoneyClass _ => Settings.MergeFIRMoney.Value,
|
||||
AmmoTemplate _ => Settings.MergeFIRMoney.Value,
|
||||
_ => Settings.MergeFIROther.Value,
|
||||
};
|
||||
|
||||
__result = __instance.TemplateId == other.TemplateId && __instance.Id != other.Id && (ignoreSpawnedInSession || __instance.SpawnedInSession == other.SpawnedInSession);
|
||||
return false;
|
||||
|
@@ -3,6 +3,7 @@ using EFT.InventoryLogic;
|
||||
using EFT.UI;
|
||||
using HarmonyLib;
|
||||
using SPT.Reflection.Patching;
|
||||
using SPT.Reflection.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -13,12 +14,17 @@ namespace UIFixes;
|
||||
|
||||
public static class UnloadAmmoPatches
|
||||
{
|
||||
private static UnloadAmmoBoxState UnloadState = null;
|
||||
|
||||
public static void Enable()
|
||||
{
|
||||
new TradingPlayerPatch().Enable();
|
||||
new TransferPlayerPatch().Enable();
|
||||
new UnloadScavTransferPatch().Enable();
|
||||
new NoScavStashPatch().Enable();
|
||||
|
||||
new UnloadAmmoBoxPatch().Enable();
|
||||
new QuickFindUnloadAmmoBoxPatch().Enable();
|
||||
}
|
||||
|
||||
public class TradingPlayerPatch : ModulePatch
|
||||
@@ -98,4 +104,97 @@ public static class UnloadAmmoPatches
|
||||
scavController.Inventory.Stash = null;
|
||||
}
|
||||
}
|
||||
|
||||
public class UnloadAmmoBoxPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.UnloadAmmo));
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static void Prefix(Item item)
|
||||
{
|
||||
if (item is AmmoBox)
|
||||
{
|
||||
UnloadState = new();
|
||||
}
|
||||
}
|
||||
|
||||
[PatchPostfix]
|
||||
public static void Postfix()
|
||||
{
|
||||
UnloadState = null;
|
||||
}
|
||||
}
|
||||
|
||||
public class QuickFindUnloadAmmoBoxPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(InteractionsHandlerClass), nameof(InteractionsHandlerClass.QuickFindAppropriatePlace));
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static void Prefix(Item item, TraderControllerClass controller, ref IEnumerable<LootItemClass> targets, ref InteractionsHandlerClass.EMoveItemOrder order)
|
||||
{
|
||||
if (UnloadState == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AmmoBox box = item.Parent.Container.ParentItem as AmmoBox;
|
||||
if (box == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Ammo boxes with multiple stacks will loop through this code, so we only want to move the box once
|
||||
if (UnloadState.initialized)
|
||||
{
|
||||
order = UnloadState.order;
|
||||
targets = UnloadState.targets;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Have to do this for them, since the calls to get parent will be wrong once we move the box
|
||||
if (!order.HasFlag(InteractionsHandlerClass.EMoveItemOrder.IgnoreItemParent))
|
||||
{
|
||||
LootItemClass parent = (item.GetNotMergedParent() as LootItemClass) ?? (item.GetRootMergedItem() as EquipmentClass);
|
||||
if (parent != null)
|
||||
{
|
||||
UnloadState.targets = targets = order.HasFlag(InteractionsHandlerClass.EMoveItemOrder.PrioritizeParent) ?
|
||||
parent.ToEnumerable().Concat(targets).Distinct() :
|
||||
targets.Concat(parent.ToEnumerable()).Distinct();
|
||||
}
|
||||
|
||||
UnloadState.order = order |= InteractionsHandlerClass.EMoveItemOrder.IgnoreItemParent;
|
||||
}
|
||||
|
||||
var operation = InteractionsHandlerClass.Move(box, UnloadState.fakeStash.Grid.FindLocationForItem(box), controller, false);
|
||||
operation.Value.RaiseEvents(controller, CommandStatus.Begin);
|
||||
operation.Value.RaiseEvents(controller, CommandStatus.Succeed);
|
||||
|
||||
UnloadState.initialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class UnloadAmmoBoxState
|
||||
{
|
||||
public StashClass fakeStash;
|
||||
public TraderControllerClass fakeController;
|
||||
|
||||
public bool initialized;
|
||||
public InteractionsHandlerClass.EMoveItemOrder order;
|
||||
public IEnumerable<LootItemClass> targets;
|
||||
|
||||
public UnloadAmmoBoxState()
|
||||
{
|
||||
fakeStash = (StashClass)Singleton<ItemFactory>.Instance.CreateItem("FakeStash", "566abbc34bdc2d92178b4576", null);
|
||||
|
||||
var profile = PatchConstants.BackEndSession.Profile;
|
||||
fakeController = new(fakeStash, profile.ProfileId, profile.Nickname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
33
Patches/UnlockCursorPatch.cs
Normal file
33
Patches/UnlockCursorPatch.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using HarmonyLib;
|
||||
using SPT.Reflection.Patching;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UIFixes;
|
||||
|
||||
public class UnlockCursorPatch : ModulePatch
|
||||
{
|
||||
private static readonly FullScreenMode[] WindowedModes = [FullScreenMode.Windowed, FullScreenMode.MaximizedWindow, FullScreenMode.FullScreenWindow];
|
||||
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(CursorManager), nameof(CursorManager.SetCursorLockMode));
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static bool Prefix(bool cursorVisible, FullScreenMode fullscreenMode, Action ___action_0)
|
||||
{
|
||||
Cursor.lockState = cursorVisible ?
|
||||
Settings.UnlockCursor.Value && WindowedModes.Contains(fullscreenMode) ? CursorLockMode.None : CursorLockMode.Confined :
|
||||
CursorLockMode.Locked;
|
||||
|
||||
if (___action_0 != null)
|
||||
{
|
||||
___action_0();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -47,7 +47,7 @@ public class Plugin : BaseUnityPlugin
|
||||
new LoadMagPresetsPatch().Enable();
|
||||
KeepWindowsOnScreenPatches.Enable();
|
||||
ContextMenuShortcutPatches.Enable();
|
||||
new OpenSortingTablePatch().Enable();
|
||||
OpenSortingTablePatches.Enable();
|
||||
LoadAmmoInRaidPatches.Enable();
|
||||
MultiSelectPatches.Enable();
|
||||
new FixUnloadLastBulletPatch().Enable();
|
||||
@@ -65,6 +65,9 @@ public class Plugin : BaseUnityPlugin
|
||||
MoveSortingTablePatches.Enable();
|
||||
FilterOutOfStockPatches.Enable();
|
||||
SortPatches.Enable();
|
||||
ReloadInPlacePatches.Enable();
|
||||
BarterOfferPatches.Enable();
|
||||
new UnlockCursorPatch().Enable();
|
||||
}
|
||||
|
||||
public static bool InRaid()
|
||||
|
@@ -28,12 +28,16 @@ Existing SPT features made better
|
||||
|
||||
- Rebind Home/End, PageUp/PageDown to work like you would expect
|
||||
- Customizable mouse scrolling speed
|
||||
- Moving stacks into containers always moves entire stack
|
||||
- ✨ Items made stackable by other mods follow normal stacking behavior
|
||||
- Allow found in raid money and ammo automatically stack with non-found-in-raid items
|
||||
- Synchronize stash scroll position everywhere your stash is visible
|
||||
- Insure and repair items directly from the context menu
|
||||
- Load ammo via context menu _in raid_
|
||||
- Load ammo preset will pull ammo from inventory, not just stash
|
||||
- ✨ Multi-grid vest and backpack grids reordered to be left to right, top to bottom.
|
||||
- ✨ Sorting will stack and combine stacks of items
|
||||
- ✨ Shift-clicking sort will only sort loose items, leaving containers in place
|
||||
|
||||
#### Inspect windows
|
||||
|
||||
@@ -57,6 +61,8 @@ Existing SPT features made better
|
||||
- Option to keep the Add Offer window open after placing your offer
|
||||
- Set prices in the Add Offer window by clicking the min/avg/max market prices (multiplies for bulk orders)
|
||||
- Autoselect Similar checkbox is remembered across sessions and application restarts
|
||||
- ✨ Replace barter offers icons with actual item images, plus owned/required counts on expansion
|
||||
- ✨ Clears filters for you when you type in search bar and there's no match
|
||||
|
||||
#### Weapon modding/presets
|
||||
|
||||
@@ -70,6 +76,7 @@ Existing SPT features made better
|
||||
|
||||
#### In raid
|
||||
|
||||
- ✨ Reloading will swap magazines in-place, instead of dropping them on the ground when there's no room.
|
||||
- ✨ Grenade quickbinds will transfer to the next grenade of the same type after throwing.
|
||||
- ✨ Option to change the behavior of the grenade key from selecting a random grenade to a deterministic one
|
||||
|
||||
|
98
Settings.cs
98
Settings.cs
@@ -2,6 +2,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UIFixes;
|
||||
@@ -47,6 +48,7 @@ internal class Settings
|
||||
private const string FleaMarketSection = "6. Flea Market";
|
||||
|
||||
// General
|
||||
public static ConfigEntry<bool> UnlockCursor { get; set; }
|
||||
public static ConfigEntry<WeaponPresetConfirmationOption> ShowPresetConfirmations { get; set; }
|
||||
public static ConfigEntry<TransferConfirmationOption> ShowTransferConfirmations { get; set; }
|
||||
public static ConfigEntry<bool> KeepMessagesOpen { get; set; }
|
||||
@@ -62,6 +64,7 @@ internal class Settings
|
||||
public static ConfigEntry<int> MouseScrollMulti { get; set; }
|
||||
public static ConfigEntry<KeyboardShortcut> InspectKeyBind { get; set; }
|
||||
public static ConfigEntry<KeyboardShortcut> OpenKeyBind { get; set; }
|
||||
public static ConfigEntry<KeyboardShortcut> ExamineKeyBind { get; set; }
|
||||
public static ConfigEntry<KeyboardShortcut> TopUpKeyBind { get; set; }
|
||||
public static ConfigEntry<KeyboardShortcut> UseKeyBind { get; set; }
|
||||
public static ConfigEntry<KeyboardShortcut> UseAllKeyBind { get; set; }
|
||||
@@ -69,6 +72,7 @@ internal class Settings
|
||||
public static ConfigEntry<KeyboardShortcut> UnpackKeyBind { get; set; }
|
||||
public static ConfigEntry<KeyboardShortcut> FilterByKeyBind { get; set; }
|
||||
public static ConfigEntry<KeyboardShortcut> LinkedSearchKeyBind { get; set; }
|
||||
public static ConfigEntry<KeyboardShortcut> SortingTableKeyBind { get; set; }
|
||||
public static ConfigEntry<bool> UseRaidMouseScrollMulti { get; set; } // Advanced
|
||||
public static ConfigEntry<int> MouseScrollMultiInRaid { get; set; } // Advanced
|
||||
public static ConfigEntry<bool> ItemContextBlocksTextInputs { get; set; } // Advanced
|
||||
@@ -81,6 +85,8 @@ internal class Settings
|
||||
public static ConfigEntry<MultiSelectStrategy> MultiSelectStrat { get; set; }
|
||||
public static ConfigEntry<bool> ShowMultiSelectDebug { get; set; } // Advanced
|
||||
public static ConfigEntry<bool> SwapItems { get; set; }
|
||||
public static ConfigEntry<bool> SwapMags { get; set; }
|
||||
public static ConfigEntry<bool> AlwaysSwapMags { get; set; }
|
||||
public static ConfigEntry<bool> SwapImpossibleContainers { get; set; }
|
||||
public static ConfigEntry<bool> ReorderGrids { get; set; }
|
||||
public static ConfigEntry<bool> SynchronizeStashScrolling { get; set; }
|
||||
@@ -90,6 +96,7 @@ internal class Settings
|
||||
public static ConfigEntry<bool> MergeFIRAmmo { get; set; }
|
||||
public static ConfigEntry<bool> MergeFIROther { get; set; }
|
||||
public static ConfigEntry<bool> AutoOpenSortingTable { get; set; }
|
||||
public static ConfigEntry<bool> DefaultSortingTableBind { get; set; } // Advanced
|
||||
public static ConfigEntry<bool> ContextMenuOnRight { get; set; }
|
||||
public static ConfigEntry<bool> ShowGPCurrency { get; set; }
|
||||
public static ConfigEntry<bool> ShowOutOfStockCheckbox { get; set; }
|
||||
@@ -113,9 +120,11 @@ internal class Settings
|
||||
|
||||
// Flea Market
|
||||
public static ConfigEntry<bool> EnableFleaHistory { get; set; }
|
||||
public static ConfigEntry<bool> ShowBarterIcons { 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> ClearFiltersOnSearch { get; set; }
|
||||
public static ConfigEntry<bool> KeepAddOfferOpen { get; set; }
|
||||
public static ConfigEntry<KeyboardShortcut> PurchaseAllKeybind { get; set; }
|
||||
public static ConfigEntry<bool> KeepAddOfferOpenIgnoreMaxOffers { get; set; } // Advanced
|
||||
@@ -126,6 +135,15 @@ internal class Settings
|
||||
var configEntries = new List<ConfigEntryBase>();
|
||||
|
||||
// General
|
||||
configEntries.Add(UnlockCursor = config.Bind(
|
||||
GeneralSection,
|
||||
"Unlock Cursor",
|
||||
true,
|
||||
new ConfigDescription(
|
||||
"Unlock cursor in Windowed, Maximized Windowed, and FullScreen Windowed modes. Note that you must alt-tab out of the game and back in for this to take affect.",
|
||||
null,
|
||||
new ConfigurationManagerAttributes { })));
|
||||
|
||||
configEntries.Add(ShowPresetConfirmations = config.Bind(
|
||||
GeneralSection,
|
||||
"Show Weapon Preset Confirmation Dialog",
|
||||
@@ -262,6 +280,15 @@ internal class Settings
|
||||
null,
|
||||
new ConfigurationManagerAttributes { })));
|
||||
|
||||
configEntries.Add(ExamineKeyBind = config.Bind(
|
||||
InputSection,
|
||||
"Examine/Interact Shortcut",
|
||||
new KeyboardShortcut(KeyCode.None),
|
||||
new ConfigDescription(
|
||||
"Keybind to examine an item, fold it, unfold it, turn it on, turn it off, or check a magazine",
|
||||
null,
|
||||
new ConfigurationManagerAttributes { })));
|
||||
|
||||
configEntries.Add(TopUpKeyBind = config.Bind(
|
||||
InputSection,
|
||||
"Top Up Ammo Shortcut",
|
||||
@@ -325,6 +352,15 @@ internal class Settings
|
||||
null,
|
||||
new ConfigurationManagerAttributes { })));
|
||||
|
||||
configEntries.Add(SortingTableKeyBind = config.Bind(
|
||||
InputSection,
|
||||
"Transfer to/from Sorting Table",
|
||||
new KeyboardShortcut(KeyCode.None),
|
||||
new ConfigDescription(
|
||||
"Keybind to transfer items to and from the sorting table. Will auto-open sorting table if necessary.",
|
||||
null,
|
||||
new ConfigurationManagerAttributes { })));
|
||||
|
||||
configEntries.Add(ItemContextBlocksTextInputs = config.Bind(
|
||||
InputSection,
|
||||
"Block Text Inputs on Item Mouseover",
|
||||
@@ -398,6 +434,24 @@ internal class Settings
|
||||
null,
|
||||
new ConfigurationManagerAttributes { })));
|
||||
|
||||
configEntries.Add(SwapMags = config.Bind(
|
||||
InventorySection,
|
||||
"Reload Magazines In-Place",
|
||||
true,
|
||||
new ConfigDescription(
|
||||
"When reloading a weapon with a magazine, swap locations with the new magazine if necessary (and possible)",
|
||||
null,
|
||||
new ConfigurationManagerAttributes { })));
|
||||
|
||||
configEntries.Add(AlwaysSwapMags = config.Bind(
|
||||
InventorySection,
|
||||
"Always Reload Magazines In-Place",
|
||||
false,
|
||||
new ConfigDescription(
|
||||
"Always reload magazines in-place, even if there's space not to. Note that in-place reloads are slower.",
|
||||
null,
|
||||
new ConfigurationManagerAttributes { })));
|
||||
|
||||
configEntries.Add(SwapImpossibleContainers = config.Bind(
|
||||
InventorySection,
|
||||
"Swap with Incompatible Containers",
|
||||
@@ -479,6 +533,15 @@ internal class Settings
|
||||
null,
|
||||
new ConfigurationManagerAttributes { })));
|
||||
|
||||
configEntries.Add(DefaultSortingTableBind = config.Bind(
|
||||
InventorySection,
|
||||
"Shift-Click to Sorting Table",
|
||||
true,
|
||||
new ConfigDescription(
|
||||
"This setting lets you enable/disable the default Tarkov behavior of shift-clicking items to transfer them to the sorting table.",
|
||||
null,
|
||||
new ConfigurationManagerAttributes { IsAdvanced = true })));
|
||||
|
||||
configEntries.Add(ContextMenuOnRight = config.Bind(
|
||||
InventorySection,
|
||||
"Context Menu Flyout on Right",
|
||||
@@ -635,6 +698,15 @@ internal class Settings
|
||||
null,
|
||||
new ConfigurationManagerAttributes { })));
|
||||
|
||||
configEntries.Add(ShowBarterIcons = config.Bind(
|
||||
FleaMarketSection,
|
||||
"Show Barter Icons",
|
||||
true,
|
||||
new ConfigDescription(
|
||||
"Show item icons for barters instead of the generic barter icon",
|
||||
null,
|
||||
new ConfigurationManagerAttributes { })));
|
||||
|
||||
configEntries.Add(EnableSlotSearch = config.Bind(
|
||||
FleaMarketSection,
|
||||
"Enable Linked Slot Search",
|
||||
@@ -653,6 +725,15 @@ internal class Settings
|
||||
null,
|
||||
new ConfigurationManagerAttributes { })));
|
||||
|
||||
configEntries.Add(ClearFiltersOnSearch = config.Bind(
|
||||
FleaMarketSection,
|
||||
"Clear Filters on Search",
|
||||
true,
|
||||
new ConfigDescription(
|
||||
"Pressing Enter after typing in the flea search bar will clear non-default filters",
|
||||
null,
|
||||
new ConfigurationManagerAttributes { })));
|
||||
|
||||
configEntries.Add(ShowRequiredQuest = config.Bind(
|
||||
FleaMarketSection,
|
||||
"Show Required Quest for Locked Offers",
|
||||
@@ -793,10 +874,25 @@ public static class SettingExtensions
|
||||
configEntry.SettingChanged += (_, _) => onChange(configEntry.Value);
|
||||
}
|
||||
|
||||
|
||||
public static void Bind<T>(this ConfigEntry<T> configEntry, Action<T> onChange)
|
||||
{
|
||||
configEntry.Subscribe(onChange);
|
||||
onChange(configEntry.Value);
|
||||
}
|
||||
|
||||
// KeyboardShortcut methods return false if any other key is down
|
||||
public static bool IsDownIgnoreOthers(this KeyboardShortcut shortcut)
|
||||
{
|
||||
return Input.GetKeyDown(shortcut.MainKey) && shortcut.Modifiers.All(Input.GetKey);
|
||||
}
|
||||
|
||||
public static bool IsPressedIgnoreOthers(this KeyboardShortcut shortcut)
|
||||
{
|
||||
return Input.GetKey(shortcut.MainKey) && shortcut.Modifiers.All(Input.GetKey);
|
||||
}
|
||||
|
||||
public static bool IsUpIgnoreOthers(this KeyboardShortcut shortcut)
|
||||
{
|
||||
return Input.GetKeyUp(shortcut.MainKey) && shortcut.Modifiers.All(Input.GetKey);
|
||||
}
|
||||
}
|
||||
|
26
Sorter.cs
26
Sorter.cs
@@ -106,4 +106,30 @@ public static class Sorter
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
// Recreation of InteractionsHandlerClass.smethod_0, but without the out type being Stackable.
|
||||
// minimumStackSpace of 0 means complete merge only, i.e. mininumStackSpace = itemToMerge.StackObjectCount
|
||||
public static bool FindStackForMerge(IEnumerable<EFT.InventoryLogic.IContainer> containers, Item itemToMerge, out Item mergeableItem, int minimumStackSpace = 0)
|
||||
{
|
||||
if (minimumStackSpace <= 0)
|
||||
{
|
||||
minimumStackSpace = itemToMerge.StackObjectsCount;
|
||||
}
|
||||
|
||||
bool ignoreSpawnedInSession = itemToMerge.Template switch
|
||||
{
|
||||
MoneyClass _ => Settings.MergeFIRMoney.Value,
|
||||
AmmoTemplate _ => Settings.MergeFIRMoney.Value,
|
||||
_ => Settings.MergeFIROther.Value,
|
||||
};
|
||||
|
||||
mergeableItem = containers.SelectMany(x => x.Items)
|
||||
.Where(x => x != itemToMerge)
|
||||
.Where(x => x.TemplateId == itemToMerge.TemplateId)
|
||||
.Where(x => ignoreSpawnedInSession || x.SpawnedInSession == itemToMerge.SpawnedInSession)
|
||||
.Where(x => x.StackObjectsCount < x.StackMaxSize)
|
||||
.FirstOrDefault(x => minimumStackSpace <= x.StackMaxSize - x.StackObjectsCount);
|
||||
|
||||
return mergeableItem != null;
|
||||
}
|
||||
}
|
@@ -4,7 +4,7 @@
|
||||
<TargetFramework>net471</TargetFramework>
|
||||
<AssemblyName>Tyfon.UIFixes</AssemblyName>
|
||||
<Description>SPT UI Fixes</Description>
|
||||
<Version>2.2.2</Version>
|
||||
<Version>2.3.1</Version>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
@@ -104,8 +104,7 @@
|
||||
xcopy /F /Y "$(TargetPath)" "$(ProjectDir)dist\BepInEx\plugins\"
|
||||
xcopy /F /Y /S "$(ProjectDir)dist\BepInEx" "$(ProjectDir)$(PathToSPT)\BepInEx"
|
||||
xcopy /F /Y /S "$(ProjectDir)dist\user" "$(ProjectDir)$(PathToSPT)\user"
|
||||
7z a -t7z $(TargetName.Replace(".", "-"))-$(Version).7z $(ProjectDir)dist\BepInEx $(ProjectDir)dist\user
|
||||
move /Y $(TargetName.Replace(".", "-"))-$(Version).7z dist\
|
||||
7z a -tzip dist\$(TargetName.Replace(".", "-"))-$(Version).zip $(ProjectDir)dist\BepInEx $(ProjectDir)dist\user
|
||||
)' />
|
||||
</Target>
|
||||
</Project>
|
||||
|
@@ -38,7 +38,7 @@ import ignore from "ignore";
|
||||
import archiver from "archiver";
|
||||
import winston from "winston";
|
||||
|
||||
const sptPath = "/SPT/3.9.2-debug";
|
||||
const sptPath = "/SPT/3.9.3-debug";
|
||||
|
||||
// Get the command line arguments to determine whether to use verbose logging.
|
||||
const args = process.argv.slice(2);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "uifixes",
|
||||
"version": "2.2.2",
|
||||
"version": "2.3.1",
|
||||
"main": "src/mod.js",
|
||||
"license": "MIT",
|
||||
"author": "Tyfon",
|
||||
|
@@ -13,6 +13,8 @@ import type { ICloner } from "@spt/utils/cloners/ICloner";
|
||||
import { RagfairLinkedSlotItemService } from "./RagfairLinkedSlotItemService";
|
||||
|
||||
import config from "../config/config.json";
|
||||
import { RagfairOfferGenerator } from "@spt/generators/RagfairOfferGenerator";
|
||||
import { IRagfairOffer } from "@spt/models/eft/ragfair/IRagfairOffer";
|
||||
|
||||
class UIFixes implements IPreSptLoadMod {
|
||||
private databaseService: DatabaseService;
|
||||
@@ -50,6 +52,36 @@ class UIFixes implements IPreSptLoadMod {
|
||||
{ frequency: "Always" }
|
||||
);
|
||||
|
||||
// Trader offers with dogtag barter - fixed in next SPT release *after* 3.9.3
|
||||
container.afterResolution(
|
||||
"RagfairOfferGenerator",
|
||||
(_, ragfairOfferGenerator: RagfairOfferGenerator) => {
|
||||
const original = ragfairOfferGenerator["createOffer"]; // By name because protected
|
||||
|
||||
ragfairOfferGenerator["createOffer"] = (userID, time, items, barterScheme, loyalLevel, isPackOffer) => {
|
||||
const offer: IRagfairOffer = original.call(
|
||||
ragfairOfferGenerator,
|
||||
userID,
|
||||
time,
|
||||
items,
|
||||
barterScheme,
|
||||
loyalLevel,
|
||||
isPackOffer
|
||||
);
|
||||
|
||||
for (let i = 0; i < offer.requirements.length; i++) {
|
||||
if (barterScheme[i]["level"] !== undefined) {
|
||||
offer.requirements[i]["level"] = barterScheme[i]["level"];
|
||||
offer.requirements[i]["side"] = barterScheme[i]["side"];
|
||||
}
|
||||
}
|
||||
|
||||
return offer;
|
||||
};
|
||||
},
|
||||
{ frequency: "Always" }
|
||||
);
|
||||
|
||||
// Better tool return - starting production
|
||||
if (config.putToolsBack) {
|
||||
container.afterResolution(
|
||||
@@ -197,4 +229,4 @@ class UIFixes implements IPreSptLoadMod {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { mod: new UIFixes() };
|
||||
export const mod = new UIFixes();
|
||||
|
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"module": "CommonJS",
|
||||
"module": "NodeNext",
|
||||
"target": "ES2022",
|
||||
"moduleResolution": "Node10",
|
||||
"moduleResolution": "NodeNext",
|
||||
"esModuleInterop": true,
|
||||
"downlevelIteration": true,
|
||||
"experimentalDecorators": true,
|
||||
|
Reference in New Issue
Block a user