diff --git a/DrawMultiSelect.cs b/DrawMultiSelect.cs index 6f947f3..c4d09f0 100644 --- a/DrawMultiSelect.cs +++ b/DrawMultiSelect.cs @@ -42,6 +42,11 @@ namespace UIFixes } } + public void OnDisable() + { + MultiSelect.Clear(); + } + public void Update() { if (!Settings.EnableMultiSelect.Value) @@ -84,7 +89,14 @@ namespace UIFixes selectEnd = Input.mousePosition; Rect selectRect = new(selectOrigin, selectEnd - selectOrigin); - foreach (GridItemView gridItemView in GetComponentsInChildren().Concat(Singleton.Instance.GetComponentsInChildren())) + + // If not secondary, then we can kick out any non-rendered items, plus they won't be covered by the foreach below + if (!secondary) + { + MultiSelect.Prune(); + } + + foreach (GridItemView gridItemView in transform.root.GetComponentsInChildren().Concat(Singleton.Instance.GetComponentsInChildren())) { RectTransform itemTransform = gridItemView.GetComponent(); Rect itemRect = new((Vector2)itemTransform.position + itemTransform.rect.position * itemTransform.lossyScale, itemTransform.rect.size * itemTransform.lossyScale); @@ -94,48 +106,23 @@ namespace UIFixes // Otherwise, ensure it's not overlapped by window UI PointerEventData eventData = new(EventSystem.current); - // Non-absolute width/height - float width = itemRect.xMax - itemRect.xMin; - float height = itemRect.yMax - itemRect.yMin; + float widthMargin = 0.1f * (itemRect.xMax - itemRect.xMin); + float heightMargin = 0.1f * (itemRect.yMax - itemRect.yMin); - 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)) + if (IsOnTop(itemRect, itemTransform, preloaderRaycaster)) // no preloaderUI on top of this? { - MultiSelect.Deselect(gridItemView, secondary); - continue; - } + if (itemTransform.IsDescendantOf(Singleton.Instance.transform)) + { + MultiSelect.Select(gridItemView, secondary); + continue; + } - raycastResults.Clear(); - eventData.position = new Vector2(itemRect.xMin + 0.1f * width, itemRect.yMax - 0.1f * height); - preloaderRaycaster.Raycast(eventData, raycastResults); - if (raycastResults.Any() && !raycastResults[0].gameObject.transform.IsDescendantOf(itemTransform)) - { - MultiSelect.Deselect(gridItemView, secondary); - continue; + if (IsOnTop(itemRect, itemTransform, localRaycaster)) // no local UI on top of this? + { + MultiSelect.Select(gridItemView, secondary); + continue; + } } - - raycastResults.Clear(); - eventData.position = new Vector2(itemRect.xMax - 0.1f * width, itemRect.yMax - 0.1f * height); - preloaderRaycaster.Raycast(eventData, raycastResults); - if (raycastResults.Any() && !raycastResults[0].gameObject.transform.IsDescendantOf(itemTransform)) - { - MultiSelect.Deselect(gridItemView, secondary); - continue; - } - - raycastResults.Clear(); - eventData.position = new Vector2(itemRect.xMax - 0.1f * width, itemRect.yMin + 0.1f * height); - preloaderRaycaster.Raycast(eventData, raycastResults); - if (raycastResults.Any() && !raycastResults[0].gameObject.transform.IsDescendantOf(itemTransform)) - { - MultiSelect.Deselect(gridItemView, secondary); - continue; - } - - MultiSelect.Select(gridItemView, secondary); - continue; } MultiSelect.Deselect(gridItemView, secondary); @@ -175,6 +162,54 @@ namespace UIFixes GUI.DrawTexture(lineArea, selectTexture); } } + + private bool IsOnTop(Rect itemRect, Transform itemTransform, GraphicRaycaster raycaster) + { + // Otherwise, ensure it's not overlapped by window UI + PointerEventData eventData = new(EventSystem.current); + + float widthMargin = 0.1f * (itemRect.xMax - itemRect.xMin); + float heightMargin = 0.1f * (itemRect.yMax - itemRect.yMin); + + List raycastResults = []; + + // Lower left + eventData.position = new Vector2(itemRect.xMin + widthMargin, itemRect.yMin + heightMargin); + raycaster.Raycast(eventData, raycastResults); + if (raycastResults.Any() && !raycastResults[0].gameObject.transform.IsDescendantOf(itemTransform)) + { + return false; + } + + // Upper left + raycastResults.Clear(); + eventData.position = new Vector2(itemRect.xMin + widthMargin, itemRect.yMax - heightMargin); + raycaster.Raycast(eventData, raycastResults); + if (raycastResults.Any() && !raycastResults[0].gameObject.transform.IsDescendantOf(itemTransform)) + { + return false; + } + + // Upper right + raycastResults.Clear(); + eventData.position = new Vector2(itemRect.xMax - widthMargin, itemRect.yMax - heightMargin); + raycaster.Raycast(eventData, raycastResults); + if (raycastResults.Any() && !raycastResults[0].gameObject.transform.IsDescendantOf(itemTransform)) + { + return false; + } + + // Lower right + raycastResults.Clear(); + eventData.position = new Vector2(itemRect.xMax - widthMargin, itemRect.yMin + heightMargin); + raycaster.Raycast(eventData, raycastResults); + if (raycastResults.Any() && !raycastResults[0].gameObject.transform.IsDescendantOf(itemTransform)) + { + return false; + } + + return true; + } } public static class TransformExtensions diff --git a/MultiSelect.cs b/MultiSelect.cs index 8b5a6d7..c634b77 100644 --- a/MultiSelect.cs +++ b/MultiSelect.cs @@ -1,5 +1,7 @@ -using EFT.UI.DragAndDrop; +using EFT.UI; +using EFT.UI.DragAndDrop; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using TMPro; @@ -12,11 +14,8 @@ namespace UIFixes private static GameObject SelectedMarkTemplate; private static GameObject SelectedBackgroundTemplate; - private static readonly Dictionary SelectedItemViews = []; - private static readonly List SortedItemContexts = []; - - private static readonly Dictionary SecondaryItemViews = []; - private static readonly List SecondaryItemContexts = []; + private static readonly Dictionary SelectedItems = []; + private static readonly Dictionary SecondaryItems = []; public static void Initialize() { @@ -34,10 +33,11 @@ namespace UIFixes public static void Toggle(GridItemView itemView, bool secondary = false) { - var dictionary = secondary ? SecondaryItemViews : SelectedItemViews; - if (dictionary.ContainsKey(itemView)) + var dictionary = secondary ? SecondaryItems : SelectedItems; + ItemContextClass itemContext = dictionary.FirstOrDefault(x => x.Value == itemView).Key; + if (itemContext != null) { - Deselect(itemView, secondary); + Deselect(itemContext, secondary); } else { @@ -48,92 +48,131 @@ namespace UIFixes public static void Clear() { // ToList() because modifying the collection - foreach (GridItemView itemView in SelectedItemViews.Keys.ToList()) + foreach (ItemContextClass itemContext in SelectedItems.Keys.ToList()) { - Deselect(itemView); + Deselect(itemContext); } } public static void Select(GridItemView itemView, bool secondary = false) { - var dictionary = secondary ? SecondaryItemViews : SelectedItemViews; - var list = secondary ? SecondaryItemContexts : SortedItemContexts; + var dictionary = secondary ? SecondaryItems : SelectedItems; - if (itemView.IsSelectable() && !SelectedItemViews.ContainsKey(itemView) && !SecondaryItemViews.ContainsKey(itemView)) + if (itemView.IsSelectable() && !SelectedItems.Any(x => x.Key.Item == itemView.Item) && !SecondaryItems.Any(x => x.Key.Item == itemView.Item)) { ItemContextClass itemContext = new MultiSelectItemContext(itemView.ItemContext, itemView.ItemRotation); - itemContext.GClass2813_0.OnDisposed += RugPull; - itemContext.OnDisposed += RugPull; // Remove event handlers that no one cares about and cause stack overflows itemContext.method_1(); - dictionary.Add(itemView, itemContext); - list.Add(itemContext); + // Subscribe to window closures to deselect + GClass3085 windowContext = itemView.GetComponentInParent()?.WindowContext ?? itemView.GetComponentInParent()?.WindowContext; + if (windowContext != null) + { + windowContext.OnClose += () => Deselect(itemContext); + } + + dictionary.Add(itemContext, itemView); ShowSelection(itemView); } } + public static void Deselect(ItemContextClass itemContext, bool secondary = false) + { + var dictionary = secondary ? SecondaryItems : SelectedItems; + + if (dictionary.TryGetValue(itemContext, out GridItemView itemView)) + { + HideSelection(itemView); + } + + dictionary.Remove(itemContext); + itemContext.Dispose(); + } + public static void Deselect(GridItemView itemView, bool secondary = false) { - var dictionary = secondary ? SecondaryItemViews : SelectedItemViews; - var list = secondary ? SecondaryItemContexts : SortedItemContexts; + var dictionary = secondary ? SecondaryItems : SelectedItems; - if (dictionary.TryGetValue(itemView, out ItemContextClass itemContext)) + ItemContextClass itemContext = dictionary.FirstOrDefault(x => x.Value == itemView).Key; + if (itemContext != null) { - itemContext.GClass2813_0.OnDisposed -= RugPull; - itemContext.OnDisposed -= RugPull; + dictionary.Remove(itemContext); itemContext.Dispose(); - list.Remove(itemContext); - dictionary.Remove(itemView); HideSelection(itemView); } } + public static void OnKillItemView(GridItemView itemView) + { + ItemContextClass itemContext = SelectedItems.FirstOrDefault(x => x.Value == itemView).Key; + if (itemContext != null) + { + SelectedItems[itemContext] = null; + HideSelection(itemView); + } + } + + public static void OnNewItemView(GridItemView itemView) + { + ItemContextClass itemContext = SelectedItems.FirstOrDefault(x => x.Key.Item == itemView.Item).Key; + if (itemContext != null) + { + // We need to refresh the context because if the item moved, it has a new address + Deselect(itemContext); + Select(itemView); + } + } + public static bool IsSelected(GridItemView itemView) { - return SelectedItemViews.ContainsKey(itemView); + return SelectedItems.Any(x => x.Key.Item == itemView.Item); + } + + public static void Prune() + { + foreach (var entry in SelectedItems.ToList()) + { + if (entry.Value == null) + { + Deselect(entry.Key); + } + } } public static void CombineSecondary() { - foreach (var entry in SecondaryItemViews) + foreach (var entry in SecondaryItems) { - SelectedItemViews.Add(entry.Key, entry.Value); + SelectedItems.Add(entry.Key, entry.Value); } - foreach (ItemContextClass itemContext in SecondaryItemContexts) - { - SortedItemContexts.Add(itemContext); - } - - SecondaryItemViews.Clear(); - SecondaryItemContexts.Clear(); + SecondaryItems.Clear(); } public static IEnumerable ItemContexts { - get { return SortedItemContexts; } + get { return SelectedItems.Keys; } } public static IEnumerable SecondaryContexts { - get { return SecondaryItemContexts; } + get { return SecondaryItems.Keys; } } public static int Count { - get { return SelectedItemViews.Count; } + get { return SelectedItems.Count; } } public static int SecondaryCount { - get { return SecondaryItemViews.Count; } + get { return SecondaryItems.Count; } } public static bool Active { - get { return SelectedItemViews.Count > 0; } + get { return SelectedItems.Count > 0; } } public static void ShowDragCount(DraggedItemView draggedItemView) @@ -157,11 +196,6 @@ 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; @@ -186,6 +220,11 @@ namespace UIFixes private static void HideSelection(GridItemView itemView) { + if (itemView == null) + { + return; + } + GameObject selectedMark = itemView.transform.Find("SelectedMark")?.gameObject; GameObject selectedBackground = itemView.transform.Find("SelectedBackground")?.gameObject; @@ -222,3 +261,4 @@ namespace UIFixes } } } + diff --git a/Patches/MultiSelectPatches.cs b/Patches/MultiSelectPatches.cs index 7841197..9f96e83 100644 --- a/Patches/MultiSelectPatches.cs +++ b/Patches/MultiSelectPatches.cs @@ -31,18 +31,21 @@ namespace UIFixes // Prevents QuickFind from attempting a merge private static bool DisableMerge = false; + // Specific type of TaskSerializer because Unity can't understand generics + public class ItemContextTaskSerializer : TaskSerializer { } + public static void Enable() { new InitializeCommonUIPatch().Enable(); new InitializeMenuUIPatch().Enable(); new SelectOnMouseDownPatch().Enable(); - new SelectMovedItemViewPatch().Enable(); new DeselectOnGridItemViewClickPatch().Enable(); new DeselectOnTradingItemViewClickPatch().Enable(); - new DeselectOnItemViewKillPatch().Enable(); + new HandleItemViewInitPatch().Enable(); + new HandleItemViewKillPatch().Enable(); new BeginDragPatch().Enable(); new EndDragPatch().Enable(); - new InspectWindowHack().Enable(); + //new InspectWindowHack().Enable(); new DisableSplitPatch().Enable(); new DisableSplitTargetPatch().Enable(); @@ -188,7 +191,27 @@ namespace UIFixes } } - public class DeselectOnItemViewKillPatch : ModulePatch + public class HandleItemViewInitPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(GridItemView), nameof(GridItemView.Init)); + } + + [PatchPostfix] + public static void Postfix(GridItemView __instance) + { + if (!MultiSelect.Active) + { + return; + } + + // the itemview isn't done being initialized + __instance.WaitForEndOfFrame(() => MultiSelect.OnNewItemView(__instance)); + } + } + + public class HandleItemViewKillPatch : ModulePatch { protected override MethodBase GetTargetMethod() { @@ -205,7 +228,7 @@ namespace UIFixes if (__instance is GridItemView gridItemView) { - MultiSelect.Deselect(gridItemView); + MultiSelect.OnKillItemView(gridItemView); } } } @@ -251,7 +274,7 @@ namespace UIFixes } [PatchPrefix] - public static bool Prefix(GridView __instance, ItemContextClass itemContext, ItemContextAbstractClass targetItemContext, ref GStruct413 operation, ref bool __result) + public static bool Prefix(GridView __instance, ItemContextClass itemContext, ItemContextAbstractClass targetItemContext, ref GStruct413 operation, ref bool __result, ItemUiContext ___itemUiContext_0) { if (InPatch || !MultiSelect.Active) { @@ -306,9 +329,16 @@ namespace UIFixes FindOrigin = GetTargetGridAddress(itemContext, selectedItemContext, hoveredAddress); FindVerticalFirst = selectedItemContext.ItemRotation == ItemRotation.Vertical; - operation = targetItem != null ? - wrappedInstance.TraderController.ExecutePossibleAction(selectedItemContext, targetItem, false /* splitting */, false /* simulate */) : - wrappedInstance.TraderController.ExecutePossibleAction(selectedItemContext, __instance.SourceContext, hoveredAddress, false /* splitting */, false /* simulate */); + if (targetItem is SortingTableClass) + { + operation = ___itemUiContext_0.QuickMoveToSortingTable(selectedItemContext.Item, false /* simulate */); + } + else + { + operation = targetItem != null ? + wrappedInstance.TraderController.ExecutePossibleAction(selectedItemContext, targetItem, false /* splitting */, false /* simulate */) : + wrappedInstance.TraderController.ExecutePossibleAction(selectedItemContext, __instance.SourceContext, hoveredAddress, false /* splitting */, false /* simulate */); + } FindOrigin = null; FindVerticalFirst = false; @@ -377,8 +407,31 @@ namespace UIFixes } [PatchPrefix] - public static bool Prefix(GridView __instance, ItemContextClass itemContext, ItemContextAbstractClass targetItemContext, ref Task __result) + 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 + 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)) + { + return false; + } + + itemController.RunNetworkTransaction(operation.Value, null); + + if (___itemUiContext_0.Tooltip != null) + { + ___itemUiContext_0.Tooltip.Close(); + } + + Singleton.Instance.PlayItemSound(itemContext.Item.ItemSound, EInventorySoundType.pickup, false); + return false; + } + if (InPatch || !MultiSelect.Active) { return true; @@ -390,7 +443,12 @@ namespace UIFixes LocationInGrid hoveredLocation = __instance.CalculateItemLocation(itemContext); GClass2769 hoveredAddress = new(__instance.Grid, hoveredLocation); - var serializer = __instance.GetOrAddComponent(); + if (__instance.Grid.ParentItem is SortingTableClass) + { + targetItemContext = new GClass2817(__instance.Grid.ParentItem, EItemViewType.Empty); + } + + var serializer = __instance.GetOrAddComponent(); __result = serializer.Initialize(SortSelectedContexts(MultiSelect.ItemContexts, itemContext), ic => { FindOrigin = GetTargetGridAddress(itemContext, ic, hoveredAddress); @@ -417,7 +475,7 @@ namespace UIFixes public class AdjustQuickFindFlagsPatch : ModulePatch { // For reasons (???), BSG doesn't even define the second bit of this flags enum - private static InteractionsHandlerClass.EMoveItemOrder PartialMerge = (InteractionsHandlerClass.EMoveItemOrder)2; + private static readonly InteractionsHandlerClass.EMoveItemOrder PartialMerge = (InteractionsHandlerClass.EMoveItemOrder)2; protected override MethodBase GetTargetMethod() { @@ -443,26 +501,6 @@ namespace UIFixes } } - public class SelectMovedItemViewPatch : ModulePatch - { - protected override MethodBase GetTargetMethod() - { - return AccessTools.Method(typeof(GridItemView), nameof(GridItemView.Init)); - } - - [PatchPostfix] - public static void Postfix(GridItemView __instance) - { - if (!InPatch) - { - return; - } - - // the itemview isn't done being initialized - __instance.WaitForEndOfFrame(() => MultiSelect.Select(__instance)); - } - } - public class GridViewPickTargetPatch : ModulePatch { public static Item FallbackResult = null; @@ -479,88 +517,6 @@ namespace UIFixes } } - private static void ShowPreview(GridView gridView, ItemContextClass itemContext, GStruct413 operation) - { - if (operation.Value is not GClass2786 moveOperation || moveOperation.To is not GClass2769 gridAddress) - { - return; - } - - if (gridAddress.Grid != gridView.Grid) - { - GridView otherGridView = gridView.transform.parent.GetComponentsInChildren().FirstOrDefault(gv => gv.Grid == gridAddress.Grid); - if (otherGridView != null) - { - ShowPreview(otherGridView, itemContext, operation); - } - - return; - } - - Image preview = UnityEngine.Object.Instantiate(gridView.R().HighlightPanel, gridView.transform, false); - preview.gameObject.SetActive(true); - Previews.Add(preview); - - var itemIcon = ItemViewFactory.LoadItemIcon(itemContext.Item); - preview.sprite = itemIcon.Sprite; - preview.SetNativeSize(); - preview.color = gridView.R().TraderController.Examined(itemContext.Item) ? Color.white : new Color(0f, 0f, 0f, 0.85f); - - Quaternion quaternion = (gridAddress.LocationInGrid.r == ItemRotation.Horizontal) ? ItemViewFactory.HorizontalRotation : ItemViewFactory.VerticalRotation; - preview.transform.rotation = quaternion; - - GStruct24 itemSize = moveOperation.Item.CalculateRotatedSize(gridAddress.LocationInGrid.r); - LocationInGrid locationInGrid = gridAddress.LocationInGrid; - - RectTransform rectTransform = preview.rectTransform; - rectTransform.localScale = Vector3.one; - rectTransform.pivot = new Vector2(0.5f, 0.5f); - rectTransform.anchorMin = new Vector2(0f, 1f); - rectTransform.anchorMax = new Vector2(0f, 1f); - rectTransform.anchoredPosition = new Vector2(locationInGrid.x * 63f, -locationInGrid.y * 63f) + new Vector2(itemSize.X * 63f / 2, -itemSize.Y * 63f / 2); - - 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.gameObject.SetActive(true); - Previews.Add(background); - - preview.transform.SetAsLastSibling(); - } - - private static void HidePreviews() - { - foreach (Image preview in Previews) - { - UnityEngine.Object.Destroy(preview.gameObject); - } - - Previews.Clear(); - } - - private static GClass2769 GetTargetGridAddress( - ItemContextClass itemContext, ItemContextClass selectedItemContext, GClass2769 hoveredGridAddress) - { - if (itemContext != selectedItemContext && - itemContext.ItemAddress is GClass2769 itemGridAddress && - selectedItemContext.ItemAddress is GClass2769 selectedGridAddress && - itemGridAddress.Grid == selectedGridAddress.Grid) - { - // Shared a grid with the dragged item - try to keep position - int xDelta = selectedGridAddress.LocationInGrid.x - itemGridAddress.LocationInGrid.x; - int yDelta = selectedGridAddress.LocationInGrid.y - itemGridAddress.LocationInGrid.y; - - LocationInGrid newLocation = new(hoveredGridAddress.LocationInGrid.x + xDelta, hoveredGridAddress.LocationInGrid.y + yDelta, selectedGridAddress.LocationInGrid.r); - newLocation.x = Math.Max(0, Math.Min(hoveredGridAddress.Grid.GridWidth.Value, newLocation.x)); - newLocation.y = Math.Max(0, Math.Min(hoveredGridAddress.Grid.GridHeight.Value, newLocation.y)); - - return new GClass2769(hoveredGridAddress.Grid, newLocation); - } - - return hoveredGridAddress; - } - // Sort the items to prioritize the items that share a grid with the dragged item, prepend the dragContext as the first one private static IEnumerable SortSelectedContexts( IEnumerable selectedContexts, ItemContextClass itemContext, bool prepend = true) @@ -675,7 +631,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; }); @@ -1030,44 +986,86 @@ namespace UIFixes } } - public class TaskSerializer : MonoBehaviour + private static void ShowPreview(GridView gridView, ItemContextClass itemContext, GStruct413 operation) { - private Func func; - private Queue itemContexts; - private Task currentTask; - private TaskCompletionSource totalTask; - - public Task Initialize(IEnumerable itemContexts, Func func) + if (operation.Value is not GClass2786 moveOperation || moveOperation.To is not GClass2769 gridAddress) { - // Create new contexts because the underlying ones will be disposed when drag ends - this.itemContexts = new(itemContexts); - this.func = func; - - currentTask = Task.CompletedTask; - Update(); - - totalTask = new TaskCompletionSource(); - return totalTask.Task; + return; } - public void Update() + if (gridAddress.Grid != gridView.Grid) { - if (!currentTask.IsCompleted) + GridView otherGridView = gridView.transform.parent.GetComponentsInChildren().FirstOrDefault(gv => gv.Grid == gridAddress.Grid); + if (otherGridView != null) { - return; + ShowPreview(otherGridView, itemContext, operation); } - if (itemContexts.Any()) - { - currentTask = func(itemContexts.Dequeue()); - } - else - { - totalTask.Complete(); - func = null; - Destroy(this); - } + return; } + + Image preview = UnityEngine.Object.Instantiate(gridView.R().HighlightPanel, gridView.transform, false); + preview.gameObject.SetActive(true); + Previews.Add(preview); + + var itemIcon = ItemViewFactory.LoadItemIcon(itemContext.Item); + preview.sprite = itemIcon.Sprite; + preview.SetNativeSize(); + preview.color = gridView.R().TraderController.Examined(itemContext.Item) ? Color.white : new Color(0f, 0f, 0f, 0.85f); + + Quaternion quaternion = (gridAddress.LocationInGrid.r == ItemRotation.Horizontal) ? ItemViewFactory.HorizontalRotation : ItemViewFactory.VerticalRotation; + preview.transform.rotation = quaternion; + + GStruct24 itemSize = moveOperation.Item.CalculateRotatedSize(gridAddress.LocationInGrid.r); + LocationInGrid locationInGrid = gridAddress.LocationInGrid; + + RectTransform rectTransform = preview.rectTransform; + rectTransform.localScale = Vector3.one; + rectTransform.pivot = new Vector2(0.5f, 0.5f); + rectTransform.anchorMin = new Vector2(0f, 1f); + rectTransform.anchorMax = new Vector2(0f, 1f); + rectTransform.anchoredPosition = new Vector2(locationInGrid.x * 63f, -locationInGrid.y * 63f) + new Vector2(itemSize.X * 63f / 2, -itemSize.Y * 63f / 2); + + 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.gameObject.SetActive(true); + Previews.Add(background); + + preview.transform.SetAsLastSibling(); + } + + private static void HidePreviews() + { + foreach (Image preview in Previews) + { + UnityEngine.Object.Destroy(preview.gameObject); + } + + Previews.Clear(); + } + + private static GClass2769 GetTargetGridAddress( + ItemContextClass itemContext, ItemContextClass selectedItemContext, GClass2769 hoveredGridAddress) + { + if (itemContext != selectedItemContext && + itemContext.ItemAddress is GClass2769 itemGridAddress && + selectedItemContext.ItemAddress is GClass2769 selectedGridAddress && + itemGridAddress.Grid == selectedGridAddress.Grid) + { + // Shared a grid with the dragged item - try to keep position + int xDelta = selectedGridAddress.LocationInGrid.x - itemGridAddress.LocationInGrid.x; + int yDelta = selectedGridAddress.LocationInGrid.y - itemGridAddress.LocationInGrid.y; + + LocationInGrid newLocation = new(hoveredGridAddress.LocationInGrid.x + xDelta, hoveredGridAddress.LocationInGrid.y + yDelta, selectedGridAddress.LocationInGrid.r); + newLocation.x = Math.Max(0, Math.Min(hoveredGridAddress.Grid.GridWidth.Value, newLocation.x)); + newLocation.y = Math.Max(0, Math.Min(hoveredGridAddress.Grid.GridHeight.Value, newLocation.y)); + + return new GClass2769(hoveredGridAddress.Grid, newLocation); + } + + return hoveredGridAddress; } } } diff --git a/TaskSerializer.cs b/TaskSerializer.cs new file mode 100644 index 0000000..5fd203a --- /dev/null +++ b/TaskSerializer.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using UnityEngine; + +namespace UIFixes +{ + public class TaskSerializer : MonoBehaviour + { + private Func func; + private Queue items; + private Task currentTask; + private TaskCompletionSource totalTask; + + public Task Initialize(IEnumerable items, Func func) + { + this.items = new(items); + this.func = func; + + currentTask = Task.CompletedTask; + totalTask = new TaskCompletionSource(); + + Update(); + + return totalTask.Task; + } + + public void Update() + { + if (!currentTask.IsCompleted) + { + return; + } + + if (items.Any()) + { + currentTask = func(items.Dequeue()); + } + else + { + totalTask.Complete(); + func = null; + Destroy(this); + } + } + } +}