216 lines
7.1 KiB
C#
216 lines
7.1 KiB
C#
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using TMPro;
|
|
using Ashwild.Player;
|
|
using Ashwild.UI;
|
|
|
|
namespace Ashwild.Inventory
|
|
{
|
|
/// <summary>
|
|
/// The inventory window. It is a local UI panel (opening/closing is purely local — the item
|
|
/// data itself lives in the networked <see cref="PlayerInventory"/>), so it is driven by the
|
|
/// GameUIManager panel stack like any other panel. The controller object stays active so it can
|
|
/// build its grid when the local player spawns; Show/Hide only toggle the visual window.
|
|
/// </summary>
|
|
public class InventoryUI : UIPanel
|
|
{
|
|
/// <summary>
|
|
/// Marks this panel as the inventory: input is locked and the cursor shows, but the world
|
|
/// keeps running (unlike the pause menu).
|
|
/// </summary>
|
|
public override PanelKind Kind => PanelKind.Inventory;
|
|
|
|
[Header("References")]
|
|
[SerializeField] private GameObject inventoryPanel;
|
|
[SerializeField] private Transform slotContainer;
|
|
[SerializeField] private GameObject slotPrefab;
|
|
|
|
[Header("Drag Ghost")]
|
|
[SerializeField] private GameObject ghostObject;
|
|
[SerializeField] private Image ghostIcon;
|
|
[SerializeField] private TextMeshProUGUI ghostQuantityText;
|
|
|
|
[Header("Context Menu")]
|
|
[SerializeField] private SlotContextMenu contextMenu;
|
|
|
|
[Header("Categories")]
|
|
[SerializeField] private InventoryCategoryManager categoryManager;
|
|
|
|
[Header("Hotbar")]
|
|
[SerializeField] private HotbarUI hotbarUI;
|
|
|
|
private SlotUI[] slotUIs;
|
|
private PlayerInventory inventory;
|
|
private bool bound;
|
|
|
|
/// <summary>
|
|
/// Waits for the networked local player to spawn before building the inventory grid.
|
|
/// </summary>
|
|
private void OnEnable()
|
|
{
|
|
PlayerEvents.LocalPlayerSpawned += HandleLocalPlayerSpawned;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unsubscribes — mirrors OnEnable exactly.
|
|
/// </summary>
|
|
private void OnDisable()
|
|
{
|
|
PlayerEvents.LocalPlayerSpawned -= HandleLocalPlayerSpawned;
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
// Setup shared ghost for all SlotUIs (hotbar + inventory) — does not need the player.
|
|
SlotUI.SetupGhost(ghostObject, ghostIcon, ghostQuantityText);
|
|
inventoryPanel.SetActive(false);
|
|
|
|
// The player may already exist (late UI init); otherwise we wait for the spawn event.
|
|
if (PlayerInventory.Instance != null)
|
|
BuildInventory();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Builds the grid as soon as the local player has spawned on the network.
|
|
/// </summary>
|
|
private void HandleLocalPlayerSpawned() => BuildInventory();
|
|
|
|
/// <summary>
|
|
/// Creates the non-hotbar slot UIs from the local player's inventory; runs once.
|
|
/// </summary>
|
|
private void BuildInventory()
|
|
{
|
|
if (bound) return;
|
|
inventory = PlayerInventory.Instance;
|
|
if (inventory == null) return;
|
|
bound = true;
|
|
|
|
// Only create slots for non-hotbar indices (hotbarSize to inventorySize-1)
|
|
int nonHotbarCount = inventory.InventorySize - inventory.HotbarSize;
|
|
slotUIs = new SlotUI[nonHotbarCount];
|
|
for (int i = 0; i < nonHotbarCount; i++)
|
|
{
|
|
int slotIndex = inventory.HotbarSize + i;
|
|
GameObject slotGO = Instantiate(slotPrefab, slotContainer);
|
|
SlotUI slotUI = slotGO.GetComponent<SlotUI>();
|
|
slotUI.Initialize(slotIndex, OnSwapRequested, OnSlotClicked);
|
|
slotUIs[i] = slotUI;
|
|
}
|
|
|
|
inventory.onSlotChanged.AddListener(RefreshSlot);
|
|
RefreshAll();
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
if (inventory != null)
|
|
inventory.onSlotChanged.RemoveListener(RefreshSlot);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opens the inventory window (called by the GameUIManager panel stack).
|
|
/// </summary>
|
|
public override void Show()
|
|
{
|
|
inventoryPanel.SetActive(true);
|
|
RefreshAll();
|
|
|
|
if (categoryManager != null)
|
|
categoryManager.Open();
|
|
|
|
if (hotbarUI != null)
|
|
hotbarUI.OnInventoryOpen();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Closes the inventory window and tears down its transient UI (ghost, context menu).
|
|
/// </summary>
|
|
public override void Hide()
|
|
{
|
|
if (ghostObject != null)
|
|
ghostObject.SetActive(false);
|
|
|
|
if (contextMenu != null)
|
|
contextMenu.Hide();
|
|
|
|
if (categoryManager != null)
|
|
categoryManager.Close();
|
|
|
|
if (hotbarUI != null)
|
|
hotbarUI.OnInventoryClose();
|
|
|
|
inventoryPanel.SetActive(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Instant hide used when the manager initializes panels — just parks the window closed.
|
|
/// </summary>
|
|
public override void HideInstant()
|
|
{
|
|
if (ghostObject != null)
|
|
ghostObject.SetActive(false);
|
|
|
|
inventoryPanel.SetActive(false);
|
|
}
|
|
|
|
// Called by any SlotUI (inventory or hotbar) when a drag-drop completes
|
|
public static void OnSwapRequested(int fromIndex, int toIndex)
|
|
{
|
|
PlayerInventory inv = PlayerInventory.Instance;
|
|
InventorySlot fromSlot = inv.GetSlot(fromIndex);
|
|
InventorySlot toSlot = inv.GetSlot(toIndex);
|
|
|
|
// Same stackable item: merge
|
|
if (!fromSlot.IsEmpty && !toSlot.IsEmpty
|
|
&& fromSlot.ItemData == toSlot.ItemData
|
|
&& toSlot.CanAccept(fromSlot.ItemData))
|
|
{
|
|
int leftover = toSlot.AddQuantity(fromSlot.Quantity);
|
|
if (leftover <= 0)
|
|
fromSlot.Clear();
|
|
else
|
|
fromSlot.Set(fromSlot.ItemData, leftover);
|
|
|
|
inv.onSlotChanged?.Invoke(fromIndex);
|
|
inv.onSlotChanged?.Invoke(toIndex);
|
|
}
|
|
else
|
|
{
|
|
// Swap
|
|
inv.SwapSlots(fromIndex, toIndex);
|
|
}
|
|
}
|
|
|
|
private void OnSlotClicked(int index, bool rightClick)
|
|
{
|
|
if (rightClick)
|
|
{
|
|
if (contextMenu != null)
|
|
contextMenu.Show(index);
|
|
else
|
|
inventory.UseItem(index);
|
|
}
|
|
}
|
|
|
|
private void RefreshSlot(int index)
|
|
{
|
|
if (slotUIs == null) return;
|
|
|
|
int localIndex = index - inventory.HotbarSize;
|
|
if (localIndex >= 0 && localIndex < slotUIs.Length)
|
|
slotUIs[localIndex].UpdateVisual(inventory.GetSlot(index));
|
|
}
|
|
|
|
private void RefreshAll()
|
|
{
|
|
if (slotUIs == null) return;
|
|
|
|
for (int i = 0; i < slotUIs.Length; i++)
|
|
{
|
|
int slotIndex = inventory.HotbarSize + i;
|
|
slotUIs[i].UpdateVisual(inventory.GetSlot(slotIndex));
|
|
}
|
|
}
|
|
}
|
|
}
|