diff --git a/DrawMultiSelect.cs b/DrawMultiSelect.cs index b56b764..b307c09 100644 --- a/DrawMultiSelect.cs +++ b/DrawMultiSelect.cs @@ -115,9 +115,6 @@ namespace UIFixes // 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); - if (IsOnTop(itemRect, itemTransform, preloaderRaycaster)) // no preloaderUI on top of this? { if (itemTransform.IsDescendantOf(Singleton.Instance.transform)) diff --git a/MultiGrid.cs b/MultiGrid.cs new file mode 100644 index 0000000..fde128b --- /dev/null +++ b/MultiGrid.cs @@ -0,0 +1,146 @@ +using EFT.InventoryLogic; +using EFT.UI.DragAndDrop; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +using GridItemAddress = GClass2769; + +namespace UIFixes +{ + public static class MultiGrid + { + private static readonly Dictionary> GridOffsets = []; + private static readonly Dictionary>> GridsByLocation = []; + + public static LocationInGrid GetGridLocation(GridItemAddress realAddress) + { + if (!IsMultiGrid(realAddress)) + { + return realAddress.LocationInGrid; + } + + Vector2Int gridOffset = GridOffsets[realAddress.Container.ParentItem][realAddress.Grid]; + return new LocationInGrid(realAddress.LocationInGrid.x + gridOffset.x, realAddress.LocationInGrid.y + gridOffset.y, realAddress.LocationInGrid.r); + } + + public static GridItemAddress GetRealAddress(StashGridClass originGrid, LocationInGrid multigridLocation) + { + if (!IsMultiGrid(originGrid.ParentItem)) + { + // Clamp to the actual grid + multigridLocation.x = Math.Max(0, Math.Min(originGrid.GridWidth.Value, multigridLocation.x)); + multigridLocation.y = Math.Max(0, Math.Min(originGrid.GridHeight.Value, multigridLocation.y)); + + return new GridItemAddress(originGrid, multigridLocation); + } + + var gridsByLocation = GridsByLocation[originGrid.ParentItem]; + + // Clamp to known "meta" grid + int x = Math.Max(0, Math.Min(gridsByLocation.Keys.Max(), multigridLocation.x)); + int y = Math.Max(0, Math.Min(gridsByLocation[x].Keys.Max(), multigridLocation.y)); + + // Sanity check + if (!gridsByLocation.ContainsKey(x) || !gridsByLocation[x].ContainsKey(y)) + { + // Perhaps some weird layout with gaps in the middle? Fall back to a known good + x = gridsByLocation.Keys.First(); + y = gridsByLocation[x].Keys.First(); + } + + StashGridClass grid = gridsByLocation[x][y]; + Vector2Int offsets = GridOffsets[originGrid.ParentItem][grid]; + + LocationInGrid location = new(x - offsets.x, y - offsets.y, multigridLocation.r); + return new GridItemAddress(grid, location); + } + + public static void Cache(GridView initialGridView) + { + if (initialGridView == null) + { + return; + } + + Item parent = initialGridView.Grid.ParentItem; + if (GridOffsets.ContainsKey(parent) || !IsMultiGrid(parent)) + { + return; + } + + Dictionary gridOffsets = []; + Dictionary> gridsByLocation = []; + + // Sometimes the parent's pivot is 0, 1; sometimes it's 0,0. Thanks BSG + RectTransform parentView = initialGridView.transform.parent.RectTransform(); + Vector2 parentPosition = parentView.pivot.y == 1 ? parentView.position : new Vector2(parentView.position.x, parentView.position.y + parentView.sizeDelta.y); + + GridView[] gridViews = parentView.GetComponentsInChildren(); + + Vector2 gridSize = new(64f * parentView.lossyScale.x, 64f * parentView.lossyScale.y); + + foreach (GridView gridView in gridViews) + { + // Get absolute offsets + float xOffset = gridView.transform.position.x - parentPosition.x; + float yOffset = -(gridView.transform.position.y - parentPosition.y); // invert y since grid coords are upper-left origin + + int x = (int)Math.Round(xOffset / gridSize.x, MidpointRounding.AwayFromZero); + int y = (int)Math.Round(yOffset / gridSize.y, MidpointRounding.AwayFromZero); + + gridOffsets.Add(gridView.Grid, new Vector2Int(x, y)); + + // Populate reverse lookup + for (int i = 0; i < gridView.Grid.GridWidth.Value; i++) + { + if (!gridsByLocation.ContainsKey(x + i)) + { + gridsByLocation.Add(x + i, []); + } + + var rowGrids = gridsByLocation[x + i]; + for (int j = 0; j < gridView.Grid.GridHeight.Value; j++) + { + rowGrids.Add(y + j, gridView.Grid); + } + } + } + + GridOffsets.Add(parent, gridOffsets); + GridsByLocation.Add(parent, gridsByLocation); + + // Best effort attempt at cleanup + IItemOwner owner = parent.Owner; + if (owner != null) + { + void onRemoveItem(GEventArgs3 eventArgs) + { + if (GridOffsets.ContainsKey(eventArgs.Item)) + { + GridOffsets.Remove(eventArgs.Item); + GridsByLocation.Remove(eventArgs.Item); + owner.RemoveItemEvent -= onRemoveItem; + } + }; + owner.RemoveItemEvent += onRemoveItem; + } + } + + private static bool IsMultiGrid(GridItemAddress itemAddress) + { + return IsMultiGrid(itemAddress.Container.ParentItem); + } + + private static bool IsMultiGrid(Item item) + { + if (item is not LootItemClass lootItem) + { + return false; + } + + return lootItem.Grids.Length > 1; + } + } +} diff --git a/MultiSelect.cs b/MultiSelect.cs index e33b0ac..be3b891 100644 --- a/MultiSelect.cs +++ b/MultiSelect.cs @@ -6,6 +6,8 @@ using System.Linq; using TMPro; using UnityEngine; +using GridItemAddress = GClass2769; + namespace UIFixes { public class MultiSelect @@ -79,12 +81,15 @@ namespace UIFixes ItemContextClass itemContext = new MultiSelectItemContext(itemView.ItemContext, itemView.ItemRotation); // Subscribe to window closures to deselect - GClass3085 windowContext = itemView.GetComponentInParent()?.WindowContext ?? itemView.GetComponentInParent()?.WindowContext; + var windowContext = itemView.GetComponentInParent()?.WindowContext ?? itemView.GetComponentInParent()?.WindowContext; if (windowContext != null) { windowContext.OnClose += () => Deselect(itemContext); } + // Cache the gridview in case we need it + MultiGrid.Cache(itemView.Container as GridView); + dictionary.Add(itemContext, itemView); ShowSelection(itemView); } @@ -193,14 +198,13 @@ namespace UIFixes // Can pass no itemContext, and it just sorts items by their grid order public static IEnumerable SortedItemContexts(ItemContextClass first = null, bool prepend = true) { - static int gridOrder(LocationInGrid loc, StashGridClass grid) => grid.GridWidth.Value * loc.y + loc.x; + static int gridOrder(LocationInGrid loc) => 100 * loc.y + loc.x; var result = ItemContexts .Where(ic => first == null || ic.Item != first.Item) - .OrderByDescending(ic => ic.ItemAddress is GClass2769) - .ThenByDescending(ic => first != null && first.ItemAddress is GClass2769 originalDraggedAddress && ic.ItemAddress is GClass2769 selectedGridAddress && selectedGridAddress.Grid == originalDraggedAddress.Grid) - .ThenByDescending(ic => ic.ItemAddress is GClass2769 selectedGridAddress ? selectedGridAddress.Grid.Id : null) - .ThenBy(ic => ic.ItemAddress is GClass2769 selectedGridAddress ? gridOrder(selectedGridAddress.LocationInGrid, selectedGridAddress.Grid) : 0); + .OrderByDescending(ic => ic.ItemAddress is GridItemAddress) + .ThenByDescending(ic => first != null && first.ItemAddress.Container.ParentItem == ic.ItemAddress.Container.ParentItem) + .ThenBy(ic => ic.ItemAddress is GridItemAddress selectedGridAddress ? gridOrder(MultiGrid.GetGridLocation(selectedGridAddress)) : 0); return first != null && prepend ? result.Prepend(first) : result; } diff --git a/Patches/FixTraderControllerSimulateFalsePatch.cs b/Patches/FixTraderControllerSimulateFalsePatch.cs index ec3dbca..a5f63ff 100644 --- a/Patches/FixTraderControllerSimulateFalsePatch.cs +++ b/Patches/FixTraderControllerSimulateFalsePatch.cs @@ -16,7 +16,7 @@ namespace UIFixes // Recreatign this function to add the comment section, so calling this with simulate = false doesn't break everything [PatchPrefix] [HarmonyPriority(Priority.Last)] - public static bool Prefix(TraderControllerClass __instance, ItemContextAbstractClass itemContext, Item targetItem, bool partialTransferOnly, bool simulate, ref object __result) + public static bool Prefix(TraderControllerClass __instance, ItemContextAbstractClass itemContext, Item targetItem, bool partialTransferOnly, bool simulate, ref GStruct413 __result) { TraderControllerClass.Struct754 opStruct; opStruct.targetItem = targetItem; diff --git a/Patches/MultiSelectPatches.cs b/Patches/MultiSelectPatches.cs index 4a1ba00..e58f253 100644 --- a/Patches/MultiSelectPatches.cs +++ b/Patches/MultiSelectPatches.cs @@ -17,6 +17,18 @@ using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; +using ItemOperation = GStruct413; +using MoveOperation = GClass2786; +using GridItemAddress = GClass2769; +using GridFindExtensions = GClass2503; +using BaseItemInfoInteractions = GClass3021; +using GenericItemContext = GClass2817; +using Stackable = GClass2735; +using DestroyError = GClass3320; +using NoRoomError = GClass3292; +using GridModificationsUnavailableError = StashGridClass.GClass3291; +using MoveSameSpaceError = InteractionsHandlerClass.GClass3329; + namespace UIFixes { public static class MultiSelectPatches @@ -28,7 +40,7 @@ namespace UIFixes private static readonly List Previews = []; // Point that various QuickFindPlace overrides should start at - private static GClass2769 FindOrigin = null; + private static GridItemAddress FindOrigin = null; private static bool FindVerticalFirst = false; // Prevents QuickFind from attempting a merge @@ -216,10 +228,10 @@ namespace UIFixes bool succeeded = true; DisableMerge = true; IgnoreItemParent = true; - Stack operations = new(); + Stack operations = new(); foreach (ItemContextClass selectedItemContext in MultiSelect.SortedItemContexts()) { - GStruct413 operation = itemUiContext.QuickFindAppropriatePlace(selectedItemContext, itemController, false /*forceStash*/, false /*showWarnings*/, false /*simulate*/); + ItemOperation operation = itemUiContext.QuickFindAppropriatePlace(selectedItemContext, itemController, false /*forceStash*/, false /*showWarnings*/, false /*simulate*/); if (operation.Succeeded && itemController.CanExecute(operation.Value)) { operations.Push(operation); @@ -232,7 +244,7 @@ namespace UIFixes if (operation.Value is IDestroyResult destroyResult && destroyResult.ItemsDestroyRequired) { - NotificationManagerClass.DisplayWarningNotification(new GClass3320(gridItemView.Item, destroyResult.ItemsToDestroy).GetLocalizedDescription(), ENotificationDurationType.Default); + NotificationManagerClass.DisplayWarningNotification(new DestroyError(gridItemView.Item, destroyResult.ItemsToDestroy).GetLocalizedDescription(), ENotificationDurationType.Default); succeeded = false; break; } @@ -246,10 +258,10 @@ namespace UIFixes string itemSound = gridItemView.Item.ItemSound; // We didn't simulate because we needed each result to depend on the last, but we have to undo before we actually do :S - Stack networkOps = new(); + Stack networkOps = new(); while (operations.Any()) { - GStruct413 operation = operations.Pop(); + ItemOperation operation = operations.Pop(); operation.Value.RollBack(); networkOps.Push(operation); } @@ -277,7 +289,7 @@ namespace UIFixes { protected override MethodBase GetTargetMethod() { - return AccessTools.Method(typeof(GClass3021), nameof(GClass3021.ExecuteInteractionInternal)); + return AccessTools.Method(typeof(BaseItemInfoInteractions), nameof(BaseItemInfoInteractions.ExecuteInteractionInternal)); } [PatchPrefix] @@ -426,13 +438,15 @@ namespace UIFixes } [PatchPrefix] - public static bool Prefix(GridView __instance, ItemContextClass itemContext, ItemContextAbstractClass targetItemContext, ref GStruct413 operation, ref bool __result, ItemUiContext ___itemUiContext_0) + public static bool Prefix(GridView __instance, ItemContextClass itemContext, ItemContextAbstractClass targetItemContext, ref ItemOperation operation, ref bool __result, ItemUiContext ___itemUiContext_0) { if (InPatch || !MultiSelect.Active) { return true; } + MultiGrid.Cache(__instance); + // Reimplementing this in order to control the simulate param. Need to *not* simulate, then rollback myself in order to test // multiple items going in var wrappedInstance = __instance.R(); @@ -448,7 +462,7 @@ namespace UIFixes if (targetItemContext != null && !targetItemContext.ModificationAvailable) { - operation = new StashGridClass.GClass3291(__instance.Grid); + operation = new GridModificationsUnavailableError(__instance.Grid); return false; } @@ -465,7 +479,7 @@ namespace UIFixes return false; } - GClass2769 hoveredAddress = new(__instance.Grid, hoveredLocation); + GridItemAddress hoveredAddress = new(__instance.Grid, hoveredLocation); if (!item.CheckAction(hoveredAddress)) { return false; @@ -476,9 +490,9 @@ namespace UIFixes bool isGridPlacement = targetItem == null; // If everything selected is the same type and is a stackable type, allow partial success - bool allowPartialSuccess = targetItem != null && itemContext.Item is GClass2735 && MultiSelect.ItemContexts.All(ic => ic.Item.TemplateId == itemContext.Item.TemplateId); + bool allowPartialSuccess = targetItem != null && itemContext.Item is Stackable && MultiSelect.ItemContexts.All(ic => ic.Item.TemplateId == itemContext.Item.TemplateId); - Stack operations = new(); + Stack operations = new(); foreach (ItemContextClass selectedItemContext in MultiSelect.SortedItemContexts(itemContext)) { if (Settings.GreedyStackMove.Value && !isGridPlacement && selectedItemContext.Item.StackObjectsCount > 1) @@ -508,7 +522,7 @@ namespace UIFixes } else { - if (operation.Error is GClass3292 noRoomError) + if (operation.Error is NoRoomError noRoomError) { // Wrap this error to display it operation = new(new DisplayableErrorWrapper(noRoomError)); @@ -555,19 +569,19 @@ namespace UIFixes ShowPreview(__instance, selectedItemContext, operation); } } - else if (operation.Error is InteractionsHandlerClass.GClass3329) + else if (operation.Error is MoveSameSpaceError) { // Moving item to the same place, cool, not a problem __result = true; operation = default; - if (isGridPlacement && selectedItemContext.Item.Parent is GClass2769 gridAddress) + if (isGridPlacement && selectedItemContext.Item.Parent is GridItemAddress gridAddress) { ShowPreview(__instance, selectedItemContext, gridAddress, R.GridView.ValidMoveColor); } } else { - if (operation.Error is GClass3292 noRoomError) + if (operation.Error is NoRoomError noRoomError) { // Wrap this error to display it operation = new(new DisplayableErrorWrapper(noRoomError)); @@ -651,12 +665,12 @@ namespace UIFixes DisableMerge = targetItemContext == null; LocationInGrid hoveredLocation = __instance.CalculateItemLocation(itemContext); - GClass2769 hoveredAddress = new(__instance.Grid, hoveredLocation); + GridItemAddress hoveredAddress = new(__instance.Grid, hoveredLocation); 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); + targetItemContext = new GenericItemContext(__instance.Grid.ParentItem, EItemViewType.Empty); } var serializer = __instance.gameObject.AddComponent(); @@ -686,7 +700,7 @@ namespace UIFixes { var itemController = gridView.R().TraderController; - GStruct413 operation = itemUiContext.QuickMoveToSortingTable(itemContext.Item, true); + ItemOperation operation = itemUiContext.QuickMoveToSortingTable(itemContext.Item, true); if (operation.Failed || !itemController.CanExecute(operation.Value)) { return; @@ -735,11 +749,11 @@ namespace UIFixes { protected override MethodBase GetTargetMethod() { - return AccessTools.Method(typeof(GClass2503), nameof(GClass2503.FindLocationForItem)); + return AccessTools.Method(typeof(GridFindExtensions), nameof(GridFindExtensions.FindLocationForItem)); } [PatchPrefix] - public static bool Prefix(IEnumerable grids, Item item, ref GClass2769 __result) + public static bool Prefix(IEnumerable grids, Item item, ref GridItemAddress __result) { if (!MultiSelect.Active) { @@ -807,7 +821,7 @@ namespace UIFixes } [PatchPrefix] - public static bool Prefix(SlotView __instance, ItemContextAbstractClass targetItemContext, ref GStruct413 operation, ref bool __result, InventoryControllerClass ___InventoryController) + public static bool Prefix(SlotView __instance, ItemContextAbstractClass targetItemContext, ref ItemOperation operation, ref bool __result, InventoryControllerClass ___InventoryController) { if (InPatch || !MultiSelect.Active) { @@ -819,11 +833,11 @@ namespace UIFixes if (targetItemContext != null && !targetItemContext.ModificationAvailable || __instance.ParentItemContext != null && !__instance.ParentItemContext.ModificationAvailable) { - operation = new StashGridClass.GClass3291(__instance.Slot); + operation = new GridModificationsUnavailableError(__instance.Slot); return false; } - Stack operations = new(); + Stack operations = new(); foreach (ItemContextClass itemContext in MultiSelect.SortedItemContexts()) { if (!Settings.GreedyStackMove.Value || itemContext.Item.StackObjectsCount <= 1) @@ -833,7 +847,7 @@ namespace UIFixes { operations.Push(operation); } - else if (operation.Error is InteractionsHandlerClass.GClass3329) + else if (operation.Error is MoveSameSpaceError) { // Moving item to the same place, cool, not a problem __result = true; @@ -929,7 +943,7 @@ namespace UIFixes } [PatchPrefix] - public static bool Prefix(TradingTableGridView __instance, ItemContextClass itemContext, ref GStruct413 operation, ref bool __result) + public static bool Prefix(TradingTableGridView __instance, ItemContextClass itemContext, ref ItemOperation operation, ref bool __result) { if (!MultiSelect.Active) { @@ -946,11 +960,11 @@ namespace UIFixes bool firstItem = true; LocationInGrid hoveredLocation = __instance.CalculateItemLocation(itemContext); - GClass2769 hoveredAddress = new(__instance.Grid, hoveredLocation); + GridItemAddress hoveredAddress = new(__instance.Grid, hoveredLocation); DisableMerge = true; - Stack operations = new(); + Stack operations = new(); foreach (ItemContextClass selectedItemContext in MultiSelect.SortedItemContexts(itemContext)) { if (traderAssortmentController.CanPrepareItemToSell(selectedItemContext.Item)) @@ -959,7 +973,7 @@ namespace UIFixes FindVerticalFirst = selectedItemContext.ItemRotation == ItemRotation.Vertical; operation = firstItem ? - InteractionsHandlerClass.Move(selectedItemContext.Item, new GClass2769(__instance.Grid, __instance.CalculateItemLocation(selectedItemContext)), traderAssortmentController.TraderController, false) : + InteractionsHandlerClass.Move(selectedItemContext.Item, new GridItemAddress(__instance.Grid, __instance.CalculateItemLocation(selectedItemContext)), traderAssortmentController.TraderController, false) : InteractionsHandlerClass.QuickFindAppropriatePlace(selectedItemContext.Item, traderAssortmentController.TraderController, [__instance.Grid.ParentItem as LootItemClass], InteractionsHandlerClass.EMoveItemOrder.Apply, false); FindVerticalFirst = false; @@ -1023,7 +1037,7 @@ namespace UIFixes TraderAssortmentControllerClass traderAssortmentController = __instance.R().TraderAssortmentController; LocationInGrid hoveredLocation = __instance.CalculateItemLocation(itemContext); - GClass2769 hoveredAddress = new(__instance.Grid, hoveredLocation); + GridItemAddress hoveredAddress = new(__instance.Grid, hoveredLocation); itemContext.DragCancelled(); traderAssortmentController.PrepareToSell(itemContext.Item, hoveredLocation); @@ -1037,11 +1051,11 @@ namespace UIFixes FindOrigin = GetTargetGridAddress(itemContext, selectedItemContext, hoveredAddress); FindVerticalFirst = selectedItemContext.ItemRotation == ItemRotation.Vertical; - GStruct413 operation = InteractionsHandlerClass.QuickFindAppropriatePlace(selectedItemContext.Item, traderAssortmentController.TraderController, [__instance.Grid.ParentItem as LootItemClass], InteractionsHandlerClass.EMoveItemOrder.Apply, true); + ItemOperation operation = InteractionsHandlerClass.QuickFindAppropriatePlace(selectedItemContext.Item, traderAssortmentController.TraderController, [__instance.Grid.ParentItem as LootItemClass], InteractionsHandlerClass.EMoveItemOrder.Apply, true); FindVerticalFirst = false; - if (operation.Failed || operation.Value is not GClass2786 moveOperation || moveOperation.To is not GClass2769 gridAddress) + if (operation.Failed || operation.Value is not MoveOperation moveOperation || moveOperation.To is not GridItemAddress gridAddress) { break; } @@ -1128,7 +1142,7 @@ namespace UIFixes { protected override MethodBase GetTargetMethod() { - return AccessTools.Method(typeof(GClass2503), nameof(GClass2503.FindLocationForItem)); + return AccessTools.Method(typeof(GridFindExtensions), nameof(GridFindExtensions.FindLocationForItem)); } [PatchPrefix] @@ -1250,9 +1264,9 @@ namespace UIFixes } } - private static void ShowPreview(GridView gridView, ItemContextClass itemContext, GStruct413 operation) + private static void ShowPreview(GridView gridView, ItemContextClass itemContext, ItemOperation operation) { - if (operation.Value is not GClass2786 moveOperation || moveOperation.To is not GClass2769 gridAddress) + if (operation.Value is not MoveOperation moveOperation || moveOperation.To is not GridItemAddress gridAddress) { return; } @@ -1273,7 +1287,7 @@ namespace UIFixes ShowPreview(gridView, itemContext, gridAddress, backgroundColor); } - private static void ShowPreview(GridView gridView, ItemContextClass itemContext, GClass2769 gridAddress, Color backgroundColor) + private static void ShowPreview(GridView gridView, ItemContextClass itemContext, GridItemAddress gridAddress, Color backgroundColor) { Image preview = UnityEngine.Object.Instantiate(gridView.R().HighlightPanel, gridView.transform, false); preview.gameObject.SetActive(true); @@ -1287,7 +1301,7 @@ namespace UIFixes Quaternion quaternion = (gridAddress.LocationInGrid.r == ItemRotation.Horizontal) ? ItemViewFactory.HorizontalRotation : ItemViewFactory.VerticalRotation; preview.transform.rotation = quaternion; - GStruct24 itemSize = itemContext.Item.CalculateRotatedSize(gridAddress.LocationInGrid.r); + var itemSize = itemContext.Item.CalculateRotatedSize(gridAddress.LocationInGrid.r); LocationInGrid locationInGrid = gridAddress.LocationInGrid; RectTransform rectTransform = preview.rectTransform; @@ -1316,8 +1330,8 @@ namespace UIFixes Previews.Clear(); } - private static GClass2769 GetTargetGridAddress( - ItemContextClass itemContext, ItemContextClass selectedItemContext, GClass2769 hoveredGridAddress) + private static GridItemAddress GetTargetGridAddress( + ItemContextClass itemContext, ItemContextClass selectedItemContext, GridItemAddress hoveredGridAddress) { if (Settings.MultiSelectStrat.Value == MultiSelectStrategy.FirstOpenSpace) { @@ -1326,19 +1340,21 @@ namespace UIFixes if (Settings.MultiSelectStrat.Value == MultiSelectStrategy.OriginalSpacing && itemContext != selectedItemContext && - itemContext.ItemAddress is GClass2769 itemGridAddress && - selectedItemContext.ItemAddress is GClass2769 selectedGridAddress && - itemGridAddress.Grid == selectedGridAddress.Grid) + itemContext.ItemAddress is GridItemAddress itemGridAddress && + selectedItemContext.ItemAddress is GridItemAddress selectedGridAddress && + itemGridAddress.Container.ParentItem == selectedGridAddress.Container.ParentItem) { - // 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; + // Shared a parent with the dragged item - try to keep position + LocationInGrid itemLocation = MultiGrid.GetGridLocation(itemGridAddress); + LocationInGrid selectedLocation = MultiGrid.GetGridLocation(selectedGridAddress); + LocationInGrid hoveredLocation = MultiGrid.GetGridLocation(hoveredGridAddress); - 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)); + int xDelta = selectedLocation.x - itemLocation.x; + int yDelta = selectedLocation.y - itemLocation.y; - return new GClass2769(hoveredGridAddress.Grid, newLocation); + LocationInGrid newLocation = new(hoveredLocation.x + xDelta, hoveredLocation.y + yDelta, selectedLocation.r); + + return MultiGrid.GetRealAddress(hoveredGridAddress.Grid, newLocation); } return hoveredGridAddress;