From b562a8665da5b1a6aade54bf2d715e5b686e5da9 Mon Sep 17 00:00:00 2001 From: Tyfon <29051038+tyfon7@users.noreply.github.com> Date: Sat, 15 Jun 2024 17:10:58 -0700 Subject: [PATCH] multiselect debug, things seem good? --- DrawMultiSelect.cs | 20 ++++++--- MultiSelect.cs | 63 ++++++++++++---------------- MultiSelectDebug.cs | 50 ++++++++++++++++++++++ Patches/MultiSelectPatches.cs | 78 +++++++++++++++++------------------ Settings.cs | 43 +++++++++++++++++-- UIFixes.csproj | 3 ++ 6 files changed, 173 insertions(+), 84 deletions(-) create mode 100644 MultiSelectDebug.cs diff --git a/DrawMultiSelect.cs b/DrawMultiSelect.cs index 2459e0a..eae5e86 100644 --- a/DrawMultiSelect.cs +++ b/DrawMultiSelect.cs @@ -17,6 +17,7 @@ namespace UIFixes private Vector3 selectOrigin; private Vector3 selectEnd; + private GraphicRaycaster localRaycaster; private GraphicRaycaster preloaderRaycaster; private bool drawing; @@ -27,6 +28,12 @@ namespace UIFixes selectTexture.SetPixel(0, 0, new Color(1f, 1f, 1f, 0.6f)); selectTexture.Apply(); + localRaycaster = GetComponentInParent(); + if (localRaycaster == null) + { + throw new InvalidOperationException("DrawMultiSelect couldn't find GraphicRayCaster in parents"); + } + preloaderRaycaster = Singleton.Instance.transform.GetChild(0).GetComponent(); if (preloaderRaycaster == null) { @@ -41,13 +48,16 @@ namespace UIFixes return; } + // checking ItemUiContext is a quick and easy way to know the mouse is over an item if (Input.GetKeyDown(KeyCode.Mouse0) && ItemUiContext.Instance.R().ItemContext == null) { - PointerEventData eventData = new(EventSystem.current); - eventData.position = Input.mousePosition; + PointerEventData eventData = new(EventSystem.current) + { + position = Input.mousePosition + }; - List results = new(); - var preloaderRaycaster = Singleton.Instance.transform.GetChild(0).GetComponent(); + List results = []; + localRaycaster.Raycast(eventData, results); preloaderRaycaster.Raycast(eventData, results); foreach (GameObject gameObject in results.Select(r => r.gameObject)) @@ -86,7 +96,7 @@ namespace UIFixes float width = itemRect.xMax - itemRect.xMin; float height = itemRect.yMax - itemRect.yMin; - List raycastResults = new(); + List raycastResults = []; eventData.position = new Vector2(itemRect.xMin + 0.1f * width, itemRect.yMin + 0.1f * height); preloaderRaycaster.Raycast(eventData, raycastResults); if (raycastResults.Any() && !raycastResults[0].gameObject.transform.IsDescendantOf(itemTransform)) diff --git a/MultiSelect.cs b/MultiSelect.cs index f64c3b6..5214967 100644 --- a/MultiSelect.cs +++ b/MultiSelect.cs @@ -1,4 +1,5 @@ using EFT.UI.DragAndDrop; +using System; using System.Collections.Generic; using System.Linq; using TMPro; @@ -11,8 +12,8 @@ namespace UIFixes private static GameObject SelectedMarkTemplate; private static GameObject SelectedBackgroundTemplate; - private static readonly HashSet SelectedItemViews = []; - private static readonly List SelectedItemContexts = []; + private static readonly Dictionary SelectedItemViews = []; + private static readonly List SortedItemContexts = []; public static void Initialize() { @@ -35,7 +36,7 @@ namespace UIFixes return; } - if (SelectedItemViews.Contains(itemView)) + if (SelectedItemViews.ContainsKey(itemView)) { Deselect(itemView); } @@ -48,7 +49,7 @@ namespace UIFixes public static void Clear() { // ToList() because we'll be modifying the collection - foreach (GridItemView itemView in SelectedItemViews.ToList()) + foreach (GridItemView itemView in SelectedItemViews.Keys.ToList()) { Deselect(itemView); } @@ -56,28 +57,39 @@ namespace UIFixes public static void Select(GridItemView itemView) { - if (itemView.IsInteractable && SelectedItemViews.Add(itemView)) + if (itemView.IsInteractable && !SelectedItemViews.ContainsKey(itemView)) { + ItemContextClass itemContext = new ItemContextClass(itemView.ItemContext, itemView.ItemRotation); + itemContext.GClass2813_0.OnDisposed += RugPull; + itemContext.OnDisposed += RugPull; + + SelectedItemViews.Add(itemView, itemContext); + SortedItemContexts.Add(itemContext); ShowSelection(itemView); } } public static void Deselect(GridItemView itemView) { - if (SelectedItemViews.Remove(itemView)) + if (SelectedItemViews.TryGetValue(itemView, out ItemContextClass itemContext)) { + itemContext.GClass2813_0.OnDisposed -= RugPull; + itemContext.OnDisposed -= RugPull; + itemContext.Dispose(); + SortedItemContexts.Remove(itemContext); + SelectedItemViews.Remove(itemView); HideSelection(itemView); } } - public static IEnumerable ItemViews + public static bool IsSelected(GridItemView itemView) { - get { return SelectedItemViews; } + return SelectedItemViews.ContainsKey(itemView); } public static IEnumerable ItemContexts { - get { return SelectedItemContexts; } + get { return SortedItemContexts; } } public static int Count @@ -85,39 +97,11 @@ namespace UIFixes get { return SelectedItemViews.Count; } } - public static bool Contains(GridItemView itemView) - { - return SelectedItemViews.Contains(itemView); - } - public static bool Active { get { return SelectedItemViews.Count > 1; } } - public static bool IsSelected(GridItemView itemView) - { - return SelectedItemViews.Contains(itemView); - } - - public static void BeginDrag() - { - foreach (ItemView itemView in SelectedItemViews) - { - SelectedItemContexts.Add(new ItemContextClass(itemView.ItemContext, itemView.ItemRotation)); - } - } - - public static void EndDrag() - { - foreach(ItemContextClass itemContext in SelectedItemContexts) - { - itemContext.Dispose(); - } - - SelectedItemContexts.Clear(); - } - public static void ShowDragCount(DraggedItemView draggedItemView) { if (Count > 1) @@ -139,6 +123,11 @@ namespace UIFixes } } + private static void RugPull() + { + throw new InvalidOperationException("ItemContext disposed before MultiSelect was done with it!"); + } + private static void ShowSelection(GridItemView itemView) { GameObject selectedMark = itemView.transform.Find("SelectedMark")?.gameObject; diff --git a/MultiSelectDebug.cs b/MultiSelectDebug.cs new file mode 100644 index 0000000..e5e0344 --- /dev/null +++ b/MultiSelectDebug.cs @@ -0,0 +1,50 @@ +using System.Text; +using UnityEngine; + +namespace UIFixes +{ + public class MultiSelectDebug : MonoBehaviour + { + private GUIStyle guiStyle; + private Rect guiRect = new(20, 20, 0, 0); + + GUIContent guiContent; + + public void OnGUI() + { + if (!Settings.EnableMultiSelect.Value || !Settings.ShowMultiSelectDebug.Value) + { + return; + } + + guiStyle ??= new GUIStyle(GUI.skin.box) + { + alignment = TextAnchor.MiddleLeft, + fontSize = 14, + margin = new RectOffset(3, 3, 3, 3), + richText = true + }; + + guiContent ??= new GUIContent(); + + StringBuilder builder = new(); + + builder.Append("MultiSelect\n"); + builder.AppendFormat("Active: {1}\n", MultiSelect.Active ? "green" : "red", MultiSelect.Active); + builder.AppendFormat("Count: {0}\n", MultiSelect.Count); + builder.Append("Items:\n"); + + foreach (ItemContextClass itemContext in MultiSelect.ItemContexts) + { + builder.Append(itemContext.Item.ToString()); + builder.AppendLine(); + } + + guiContent.text = builder.ToString(); + + guiRect.size = guiStyle.CalcSize(guiContent); + + GUI.Box(guiRect, guiContent, guiStyle); + } + } +} diff --git a/Patches/MultiSelectPatches.cs b/Patches/MultiSelectPatches.cs index 1fddc45..fbfea1a 100644 --- a/Patches/MultiSelectPatches.cs +++ b/Patches/MultiSelectPatches.cs @@ -1,4 +1,5 @@ using Aki.Reflection.Patching; +using Comfort.Common; using EFT.InventoryLogic; using EFT.UI; using EFT.UI.DragAndDrop; @@ -23,14 +24,15 @@ namespace UIFixes new InitializePatch().Enable(); new SelectPatch().Enable(); new DeselectOnOtherMouseDown().Enable(); - new DeselectOnMovePatch().Enable(); + new DeselectOnItemViewKillPatch().Enable(); new BeginDragPatch().Enable(); - new EndDragPatch().Enable(); new GridViewCanAcceptPatch().Enable(); new GridViewAcceptItemPatch().Enable(); new SlotViewCanAcceptPatch().Enable(); new SlotViewAcceptItemPatch().Enable(); + + new InspectWindowHack().Enable(); } public class InitializePatch : ModulePatch @@ -50,9 +52,14 @@ namespace UIFixes MultiSelect.Initialize(); - __instance.InventoryScreen.GetOrAddComponent(); + __instance.InventoryScreen.transform.Find("Items Panel").gameObject.GetOrAddComponent(); __instance.TransferItemsInRaidScreen.GetOrAddComponent(); __instance.TransferItemsScreen.GetOrAddComponent(); + + if (Settings.ShowMultiSelectDebug.Value) + { + Singleton.Instance.GetOrAddComponent(); + } } } @@ -112,15 +119,15 @@ namespace UIFixes } } - public class DeselectOnMovePatch : ModulePatch + public class DeselectOnItemViewKillPatch : ModulePatch { protected override MethodBase GetTargetMethod() { return AccessTools.Method(typeof(ItemView), nameof(ItemView.Kill)); } - [PatchPostfix] - public static void Postfix(ItemView __instance) + [PatchPrefix] + public static void Prefix(ItemView __instance) { if (!Settings.EnableMultiSelect.Value) { @@ -141,17 +148,6 @@ namespace UIFixes return AccessTools.Method(typeof(ItemView), nameof(ItemView.OnBeginDrag)); } - [PatchPrefix] - public static void Prefix() - { - if (!Settings.EnableMultiSelect.Value) - { - return; - } - - MultiSelect.BeginDrag(); - } - [PatchPostfix] public static void Postfix(ItemView __instance) { @@ -164,25 +160,6 @@ namespace UIFixes } } - public class EndDragPatch : ModulePatch - { - protected override MethodBase GetTargetMethod() - { - return AccessTools.Method(typeof(ItemView), nameof(ItemView.OnEndDrag)); - } - - [PatchPostfix] - public static void Postfix() - { - if (!Settings.EnableMultiSelect.Value) - { - return; - } - - MultiSelect.EndDrag(); - } - } - public class GridViewCanAcceptPatch : ModulePatch { protected override MethodBase GetTargetMethod() @@ -291,7 +268,7 @@ namespace UIFixes // Reimplementing this in order to control the simulate param. Need to *not* simulate, then rollback myself in order to test // multiple items going in - if (targetItemContext != null && !targetItemContext.ModificationAvailable || + if (targetItemContext != null && !targetItemContext.ModificationAvailable || __instance.ParentItemContext != null && !__instance.ParentItemContext.ModificationAvailable) { operation = new StashGridClass.GClass3291(__instance.Slot); @@ -313,7 +290,7 @@ namespace UIFixes } // We didn't simulate so now we undo - while(operations.Any()) + while (operations.Any()) { operations.Pop().Value?.RollBack(); } @@ -349,6 +326,29 @@ namespace UIFixes } } + // The inspect window likes to recreate itself entirely when a slot is removed, which destroys all of the gridviews and + // borks the multiselect. This patch just stops it from responding until the last one (since by then the selection is down to 1, which + // is considered inactive multiselect) + public class InspectWindowHack : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(ItemSpecificationPanel), nameof(ItemSpecificationPanel.OnRemoveFromSlotEvent)); + } + + [PatchPrefix] + public static bool Prefix() + { + if (!Settings.EnableMultiSelect.Value || !MultiSelect.Active) + { + return true; + } + + // Just skip it when multiselect is active + return false; + } + } + public class TaskSerializer : MonoBehaviour { private Func func; @@ -375,7 +375,7 @@ namespace UIFixes { return; } - + if (itemContexts.Any()) { currentTask = func(itemContexts.Dequeue()); diff --git a/Settings.cs b/Settings.cs index 4720853..90335e7 100644 --- a/Settings.cs +++ b/Settings.cs @@ -55,6 +55,7 @@ namespace UIFixes // Inventory public static ConfigEntry EnableMultiSelect { get; set; } + public static ConfigEntry ShowMultiSelectDebug { get; set; } // Advanced public static ConfigEntry SwapItems { get; set; } public static ConfigEntry SwapImpossibleContainers { get; set; } public static ConfigEntry SynchronizeStashScrolling { get; set; } @@ -278,10 +279,19 @@ namespace UIFixes "Enable Multiselect", true, new ConfigDescription( - "Enable multiselect via Shift-click and drag-to-select", + "Enable multiselect via Shift-click and drag-to-select. This cannot be used together with Auto-open Sorting Table", null, new ConfigurationManagerAttributes { }))); + configEntries.Add(ShowMultiSelectDebug = config.Bind( + InventorySection, + "Show Multiselect Debug", + false, + new ConfigDescription( + "Enable multi-select debugging display", + null, + new ConfigurationManagerAttributes { IsAdvanced = true }))); + configEntries.Add(SwapItems = config.Bind( InventorySection, "Enable In-Place Item Swapping", @@ -339,9 +349,9 @@ namespace UIFixes configEntries.Add(AutoOpenSortingTable = config.Bind( InventorySection, "Auto-open Sorting Table", - true, + false, new ConfigDescription( - "Automatically open the sorting table if it's closed when you shift-click an item", + "Automatically open the sorting table if it's closed when you shift-click an item. This and Enable Multiselect cannot be used together.", null, new ConfigurationManagerAttributes { }))); @@ -502,7 +512,10 @@ namespace UIFixes new ConfigurationManagerAttributes { IsAdvanced = true }))); RecalcOrder(configEntries); + + ToggleExclusives(); } + private static void RecalcOrder(List configEntries) { // Set the Order field for all settings, to avoid unnecessary changes when adding new settings @@ -517,5 +530,29 @@ namespace UIFixes settingOrder--; } } + + private static void ToggleExclusives() + { + if (Settings.EnableMultiSelect.Value) + { + Settings.AutoOpenSortingTable.Value = false; + } + + Settings.EnableMultiSelect.SettingChanged += (_, _) => + { + if (Settings.EnableMultiSelect.Value) + { + Settings.AutoOpenSortingTable.Value = false; + } + }; + + Settings.AutoOpenSortingTable.SettingChanged += (_, _) => + { + if (Settings.AutoOpenSortingTable.Value) + { + Settings.EnableMultiSelect.Value = false; + } + }; + } } } diff --git a/UIFixes.csproj b/UIFixes.csproj index 85ad6ae..52f4f0d 100644 --- a/UIFixes.csproj +++ b/UIFixes.csproj @@ -63,6 +63,9 @@ $(PathToSPT)\EscapeFromTarkov_Data\Managed\UnityEngine.InputLegacyModule.dll + + ..\..\..\..\SPT\3.8.3-debug\EscapeFromTarkov_Data\Managed\UnityEngine.TextRenderingModule.dll + $(PathToSPT)\EscapeFromTarkov_Data\Managed\UnityEngine.UI.dll