using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.UIElements; namespace Ironspore { public sealed partial class GameHud { string selectedUid; Hold selectedHold; int selectedSlot = -1; bool rotated; int quantity; const int CellWidth = 103, CellHeight = 55; sealed class GridCell { public Hold hold; public int x, y; public CargoStack occupant; } CargoStack dragItem; Hold dragHold; int dragSlot, dragPointer; Vector2 dragStart; VisualElement dragPreview; CargoStack Selection => selectedSlot >= 0 ? game.Run.installed.Find(i => i.uid == selectedUid) : Contents(selectedHold).Find(i => i.uid == selectedUid); List Contents(Hold hold) => hold == Hold.Cargo ? game.Run.cargo : game.Run.safeCase; void SelectInventoryItem(CargoStack item, Hold hold, int slot = -1) { selectedUid = item?.uid; selectedSlot = slot; selectedHold = hold; rotated = item?.rotated ?? false; quantity = item?.count ?? 0; ShowCargo(); } void RotateSelection() { rotated = !rotated; if (dragPreview == null) ShowCargo(); else SizePreview(); } public void ShowCargo() { var panel = Overlay(L("ui.cargo.title"), L(game.Run.podUsed ? "ui.cargo.used" : "ui.cargo.help")); view = View.Cargo; var installed = Element(panel, "row"); foreach (var part in game.Run.installed) { int slot = InventoryGrid.Slot(part.itemId); var button = Button(installed, part.Name, () => SelectInventoryItem(part, Hold.Cargo, slot), part.uid == selectedUid ? "loadout selected" : "loadout"); EnableDrag(button, part, Hold.Cargo, slot); } var grids = Element(panel, "row"); DrawHold(grids, Hold.Cargo, InventoryGrid.CargoSize); DrawHold(grids, Hold.SafeCase, InventoryGrid.CaseSize); Text(panel, L("ui.cargo.capacity", game.Run.cargo.Sum(InventoryGrid.Area), game.Run.cargo.Where(i => game.SelectedCargo.Contains(i.uid)).Sum(InventoryGrid.Area)), "budget"); var item = Selection; if (item != null) { InventoryGrid.Size(item.itemId, rotated, out int w, out int h); Text(panel, L("ui.cargo.selection", item.Name, w, h, quantity), "body"); var actions = Element(panel, "row"); Button(actions, L("ui.cargo.rotate"), RotateSelection); if (item.count > 1) { Button(actions, "−", () => { quantity--; ShowCargo(); }).SetEnabled(quantity > 1); Button(actions, "+", () => { quantity++; ShowCargo(); }).SetEnabled(quantity < item.count); } if (selectedSlot < 0) { if (selectedHold == Hold.Cargo && !game.Run.podUsed) Button(actions, L(game.SelectedCargo.Contains(item.uid) ? "ui.cargo.unmark" : "ui.cargo.mark"), () => game.ToggleCargo(item.uid)); if (InventoryGrid.Slot(item.itemId) >= 0) Button(actions, L("ui.cargo.install"), () => { if (game.InstallItem(item, selectedHold)) selectedUid = null; ShowCargo(); }); } Button(actions, L("ui.cargo.cancel"), () => SelectInventoryItem(null, Hold.Cargo)); } Text(panel, game.Notice, "warning"); Button(panel, L("ui.return"), game.CloseMenu, "primary"); } void DrawHold(VisualElement parent, Hold hold, int size) { var column = Element(parent, "inventory-column"); Text(column, L(hold == Hold.Cargo ? "ui.cargo.hold" : "ui.cargo.case"), "section"); var grid = new VisualElement(); column.Add(grid); grid.style.width = size * CellWidth; grid.style.height = size * CellHeight; var items = Contents(hold); for (int y = 0; y < size; y++) for (int x = 0; x < size; x++) { int cx = x, cy = y; var occupant = items.Find(i => { InventoryGrid.Size(i.itemId, i.rotated, out int w, out int h); return cx >= i.x && cy >= i.y && cx < i.x + w && cy < i.y + h; }); var cell = Button(grid, "", () => InventoryClick(hold, cx, cy, occupant), "inventory-cell"); cell.name = hold + "-" + x + "-" + y; cell.tooltip = occupant?.Name ?? ""; cell.userData = new GridCell { hold = hold, x = x, y = y, occupant = occupant }; if (occupant != null) EnableDrag(cell, occupant, hold, -1); PositionInGrid(cell, x, y, 1, 1); } foreach (var item in items) { InventoryGrid.Size(item.itemId, item.rotated, out int w, out int h); string prefix = game.SelectedCargo.Contains(item.uid) ? "✓ " : ""; var label = Text(grid, prefix + item.Name + (item.count > 1 ? " ×" + item.count : ""), "inventory-item"); label.pickingMode = PickingMode.Ignore; if (item.uid == selectedUid) label.AddToClassList("selected"); PositionInGrid(label, item.x, item.y, w, h); } } void EnableDrag(Button button, CargoStack item, Hold hold, int slot) { // Captured pointer events can be dispatched directly to the pressed button. button.RegisterCallback(DragMove, TrickleDown.TrickleDown); button.RegisterCallback(DragEnd, TrickleDown.TrickleDown); button.RegisterCallback(evt => { if (evt.button != 0) return; dragItem = item; dragHold = hold; dragSlot = slot; dragStart = evt.position; dragPointer = evt.pointerId; }, TrickleDown.TrickleDown); } void DragMove(PointerMoveEvent evt) { if (dragItem == null || evt.pointerId != dragPointer) return; Vector2 pos = evt.position; if (dragPreview == null) { if ((pos - dragStart).sqrMagnitude < 36) return; if (selectedUid != dragItem.uid) { quantity = dragItem.count; rotated = dragItem.rotated; } selectedUid = dragItem.uid; selectedSlot = dragSlot; selectedHold = dragHold; dragPreview = Text(root, dragItem.Name, "inventory-item selected"); dragPreview.pickingMode = PickingMode.Ignore; dragPreview.style.position = UnityEngine.UIElements.Position.Absolute; SizePreview(); } var target = root.panel.Pick(pos); Vector2 anchor = target?.userData is GridCell ? target.worldBound.min : pos; Vector2 local = root.WorldToLocal(anchor); dragPreview.style.left = local.x; dragPreview.style.top = local.y; } void SizePreview() { InventoryGrid.Size(dragItem.itemId, rotated, out int w, out int h); dragPreview.style.width = CellWidth * w - 2; dragPreview.style.height = CellHeight * h - 2; } void DragEnd(PointerUpEvent evt) { if (dragItem == null || evt.pointerId != dragPointer) return; bool dragged = dragPreview != null; ResetDrag(); if (!dragged) return; evt.StopImmediatePropagation(); var owner = root.panel.GetCapturingElement(evt.pointerId) as VisualElement; owner?.ReleasePointer(evt.pointerId); var target = root.panel.Pick(evt.position)?.userData as GridCell; if (target != null) InventoryClick(target.hold, target.x, target.y, target.occupant); else ShowCargo(); } void ResetDrag() { dragPreview?.RemoveFromHierarchy(); dragPreview = null; dragItem = null; } static void PositionInGrid(VisualElement element, int x, int y, int w, int h) { element.style.position = UnityEngine.UIElements.Position.Absolute; element.style.left = x * CellWidth; element.style.top = y * CellHeight; element.style.width = w * CellWidth - 2; element.style.height = h * CellHeight - 2; } void InventoryClick(Hold hold, int x, int y, CargoStack occupant) { var item = Selection; if (item == null) { SelectInventoryItem(occupant, hold); return; } bool ok; if (selectedSlot >= 0) ok = game.Store.Uninstall(selectedSlot, hold, x, y, rotated); else if (occupant == null || item.uid == occupant.uid) ok = game.Store.MoveItem(item.uid, selectedHold, hold, x, y, rotated, quantity); else if (item.itemId == occupant.itemId && InventoryGrid.IsMaterial(item.itemId)) ok = game.Store.MergeMaterial(item.uid, selectedHold, occupant.uid, hold, quantity); else ok = quantity == item.count && game.Store.SwapItems(item.uid, selectedHold, occupant.uid, hold, rotated); if (ok) { selectedUid = null; game.SelectedCargo.RemoveAll(uid => !game.Run.cargo.Exists(i => i.uid == uid)); game.Pilot.ApplyLoadout(); } else game.Notify(string.IsNullOrEmpty(game.Store.Error) ? "notice.placement" : "notice.saveFailed"); ShowCargo(); } } }