diff --git a/DrawMultiSelect.cs b/DrawMultiSelect.cs index c4d09f0..c73e9e5 100644 --- a/DrawMultiSelect.cs +++ b/DrawMultiSelect.cs @@ -103,6 +103,12 @@ namespace UIFixes if (selectRect.Overlaps(itemRect, true)) { + // Don't re-raycast already selected items - if there were visible before they still are + if (MultiSelect.IsSelected(gridItemView, secondary)) + { + continue; + } + // Otherwise, ensure it's not overlapped by window UI PointerEventData eventData = new(EventSystem.current); diff --git a/MultiSelect.cs b/MultiSelect.cs index c634b77..02e4e38 100644 --- a/MultiSelect.cs +++ b/MultiSelect.cs @@ -1,7 +1,5 @@ using EFT.UI; using EFT.UI.DragAndDrop; -using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using TMPro; @@ -124,9 +122,10 @@ namespace UIFixes } } - public static bool IsSelected(GridItemView itemView) + public static bool IsSelected(GridItemView itemView, bool secondary = false) { - return SelectedItems.Any(x => x.Key.Item == itemView.Item); + var dictionary = secondary ? SecondaryItems : SelectedItems; + return dictionary.Any(x => x.Key.Item == itemView.Item); } public static void Prune() diff --git a/Patches/MultiSelectPatches.cs b/Patches/MultiSelectPatches.cs index 9f96e83..61eda9a 100644 --- a/Patches/MultiSelectPatches.cs +++ b/Patches/MultiSelectPatches.cs @@ -66,6 +66,7 @@ namespace UIFixes new FindLocationForItemPatch().Enable(); new FindPlaceToPutPatch().Enable(); new AdjustQuickFindFlagsPatch().Enable(); + new AllowFindSameSpotPatch().Enable(); } public class InitializeCommonUIPatch : ModulePatch @@ -351,11 +352,20 @@ namespace UIFixes ShowPreview(__instance, selectedItemContext, operation); } } + else if (operation.Error is InteractionsHandlerClass.GClass3329) + { + // Moving item to the same place, cool, not a problem + __result = true; + if (showHighlights && selectedItemContext.Item.Parent is GClass2769 gridAddress) + { + ShowPreview(__instance, selectedItemContext, gridAddress, R.GridView.ValidMoveColor); + } + } else { - // Wrap this error to display it if (operation.Error is GClass3292 noRoomError) { + // Wrap this error to display it operation = new(new DisplayableErrorWrapper(noRoomError)); } @@ -409,12 +419,12 @@ namespace UIFixes [PatchPrefix] public static bool Prefix(GridView __instance, ItemContextClass itemContext, ItemContextAbstractClass targetItemContext, ref Task __result, ItemUiContext ___itemUiContext_0) { - // Need to fully implement AcceptItem for the sorting table normally that just uses null targetItemContext + // Need to fully implement AcceptItem for the sorting table - normally that just uses null targetItemContext if (InPatch && targetItemContext?.Item is SortingTableClass) { __result = Task.CompletedTask; var itemController = __instance.R().TraderController; - + GStruct413 operation = ___itemUiContext_0.QuickMoveToSortingTable(itemContext.Item, true); if (operation.Failed || !itemController.CanExecute(operation.Value)) { @@ -445,6 +455,7 @@ namespace UIFixes if (__instance.Grid.ParentItem is SortingTableClass) { + // Sorting table will need a targetItemContext. Dunno if this is the right type but all it needs is the .Item property targetItemContext = new GClass2817(__instance.Grid.ParentItem, EItemViewType.Empty); } @@ -501,6 +512,26 @@ namespace UIFixes } } + public class AllowFindSameSpotPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(GClass2503), nameof(GClass2503.FindLocationForItem)); + } + + [PatchPrefix] + public static bool Prefix(IEnumerable grids, Item item, ref GClass2769 __result) + { + if (!MultiSelect.Active) + { + return true; + } + + __result = grids.Select(g => g.FindLocationForItem(item)).FirstOrDefault(x => x != null); + return false; + } + } + public class GridViewPickTargetPatch : ModulePatch { public static Item FallbackResult = null; @@ -597,6 +628,11 @@ namespace UIFixes { operations.Push(operation); } + else if (operation.Error is InteractionsHandlerClass.GClass3329) + { + // Moving item to the same place, cool, not a problem + __result = true; + } else { break; @@ -631,7 +667,7 @@ namespace UIFixes InPatch = true; - var serializer = __instance.GetOrAddComponent>(); + var serializer = __instance.GetOrAddComponent(); __result = serializer.Initialize(MultiSelect.ItemContexts, itemContext => __instance.AcceptItem(itemContext, targetItemContext)); __result.ContinueWith(_ => { InPatch = false; }); @@ -1004,6 +1040,13 @@ namespace UIFixes return; } + Color backgroundColor = gridView.GetHighlightColor(itemContext, operation, null); + + ShowPreview(gridView, itemContext, gridAddress, backgroundColor); + } + + private static void ShowPreview(GridView gridView, ItemContextClass itemContext, GClass2769 gridAddress, Color backgroundColor) + { Image preview = UnityEngine.Object.Instantiate(gridView.R().HighlightPanel, gridView.transform, false); preview.gameObject.SetActive(true); Previews.Add(preview); @@ -1016,7 +1059,7 @@ namespace UIFixes Quaternion quaternion = (gridAddress.LocationInGrid.r == ItemRotation.Horizontal) ? ItemViewFactory.HorizontalRotation : ItemViewFactory.VerticalRotation; preview.transform.rotation = quaternion; - GStruct24 itemSize = moveOperation.Item.CalculateRotatedSize(gridAddress.LocationInGrid.r); + GStruct24 itemSize = itemContext.Item.CalculateRotatedSize(gridAddress.LocationInGrid.r); LocationInGrid locationInGrid = gridAddress.LocationInGrid; RectTransform rectTransform = preview.rectTransform; @@ -1028,8 +1071,7 @@ namespace UIFixes Image background = UnityEngine.Object.Instantiate(preview, gridView.transform, false); background.sprite = null; - Color normalColor = gridView.GetHighlightColor(itemContext, operation, null); - background.color = new(normalColor.r, normalColor.g, normalColor.b, 0.3f); + background.color = backgroundColor; background.gameObject.SetActive(true); Previews.Add(background); @@ -1049,7 +1091,13 @@ namespace UIFixes private static GClass2769 GetTargetGridAddress( ItemContextClass itemContext, ItemContextClass selectedItemContext, GClass2769 hoveredGridAddress) { - if (itemContext != selectedItemContext && + if (Settings.MultiSelectStrat.Value == MultiSelectStrategy.FirstOpenSpace) + { + return null; + } + + if (Settings.MultiSelectStrat.Value == MultiSelectStrategy.OriginalSpacing && + itemContext != selectedItemContext && itemContext.ItemAddress is GClass2769 itemGridAddress && selectedItemContext.ItemAddress is GClass2769 selectedGridAddress && itemGridAddress.Grid == selectedGridAddress.Grid) diff --git a/Settings.cs b/Settings.cs index fcff922..275c820 100644 --- a/Settings.cs +++ b/Settings.cs @@ -19,6 +19,16 @@ namespace UIFixes Always } + internal enum MultiSelectStrategy + { + [Description("First Available Space")] + FirstOpenSpace, + [Description("Same Row or Below (Wrapping)")] + SameRowOrLower, + [Description("Keep Original Spacing (Best Effort)")] + OriginalSpacing + } + internal class Settings { // Categories @@ -55,6 +65,7 @@ namespace UIFixes // Inventory public static ConfigEntry EnableMultiSelect { get; set; } + public static ConfigEntry MultiSelectStrat { get; set; } public static ConfigEntry ShowMultiSelectDebug { get; set; } // Advanced public static ConfigEntry SwapItems { get; set; } public static ConfigEntry SwapImpossibleContainers { get; set; } @@ -283,6 +294,15 @@ namespace UIFixes null, new ConfigurationManagerAttributes { }))); + configEntries.Add(MultiSelectStrat = config.Bind( + InventorySection, + "Multiselect Item Placement", + MultiSelectStrategy.OriginalSpacing, + new ConfigDescription( + "Controls where multiselected items are placed, relative to the item being dragged. Note that original spacing only refers to items that were in the same grid.", + null, + new ConfigurationManagerAttributes { }))); + configEntries.Add(ShowMultiSelectDebug = config.Bind( InventorySection, "Show Multiselect Debug",