using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; using TMPro; using System; namespace Ashwild.Inventory { public class SlotUI : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IDropHandler, IPointerClickHandler { [Header("References")] [SerializeField] private Image iconImage; [SerializeField] private TextMeshProUGUI quantityText; [SerializeField] private Image highlightImage; [Header("Uses Bar")] [Tooltip("Root object of the uses/durability bar — shown only for items that track uses.")] [SerializeField] private GameObject usageBarRoot; [Tooltip("Filled image whose fillAmount maps to the remaining uses (Image Type = Filled).")] [SerializeField] private Image usageBarFill; [Tooltip("Icon tint applied when the item still has uses left.")] [SerializeField] private Color normalTint = Color.white; [Tooltip("Icon (and bar) tint applied when the item is depleted — a broken repairable tool / empty container.")] [SerializeField] private Color depletedTint = Color.red; private int slotIndex; private Action onSwapRequested; private Action onClicked; public int SlotIndex => slotIndex; // Static drag state shared across all slots private static SlotUI draggedSlot; private static GameObject ghostObject; private static Image ghostIcon; private static TextMeshProUGUI ghostQuantity; public static void SetupGhost(GameObject ghost, Image icon, TextMeshProUGUI qty) { ghostObject = ghost; ghostIcon = icon; ghostQuantity = qty; ghostObject.SetActive(false); } public void Initialize(int index, Action swapCallback, Action clickCallback) { slotIndex = index; onSwapRequested = swapCallback; onClicked = clickCallback; } /// /// Redraws the slot: icon, quantity, and the uses bar. Uses-tracked items (tools, multi-bite /// food) show a fill bar for their remaining uses and turn red once depleted. /// public void UpdateVisual(InventorySlot slot) { if (slot == null || slot.IsEmpty) { iconImage.gameObject.SetActive(false); quantityText.gameObject.SetActive(false); if (usageBarRoot != null) usageBarRoot.SetActive(false); } else { iconImage.gameObject.SetActive(true); iconImage.sprite = slot.ItemData.Icon; bool showQty = slot.Quantity > 1; quantityText.gameObject.SetActive(showQty); if (showQty) quantityText.text = slot.Quantity.ToString(); UpdateUsageBar(slot); } } /// /// Shows the uses bar and tints the icon red when the item is depleted; hides the bar entirely /// for items that do not track uses. Preserves the current icon alpha so a mid-drag fade stays. /// private void UpdateUsageBar(InventorySlot slot) { bool hasUses = slot.ItemData.HasUses; if (usageBarRoot != null) usageBarRoot.SetActive(hasUses); Color tint = (hasUses && slot.IsDepleted) ? depletedTint : normalTint; tint.a = iconImage.color.a; iconImage.color = tint; if (hasUses && usageBarFill != null) usageBarFill.fillAmount = (float)slot.CurrentUses / slot.ItemData.MaxUses; } public void SetSelected(bool selected) { if (highlightImage != null) highlightImage.gameObject.SetActive(selected); } // Drag & Drop public void OnBeginDrag(PointerEventData eventData) { if (eventData.button != PointerEventData.InputButton.Left) return; InventorySlot slot = PlayerInventory.Instance.GetSlot(slotIndex); if (slot == null || slot.IsEmpty) return; draggedSlot = this; // Show ghost if (ghostObject != null) { ghostObject.SetActive(true); ghostIcon.sprite = slot.ItemData.Icon; ghostIcon.gameObject.SetActive(true); bool showQty = slot.Quantity > 1; ghostQuantity.gameObject.SetActive(showQty); if (showQty) ghostQuantity.text = slot.Quantity.ToString(); ghostObject.transform.position = eventData.position; } // Make source icon semi-transparent Color c = iconImage.color; c.a = 0.4f; iconImage.color = c; } public void OnDrag(PointerEventData eventData) { if (draggedSlot != this) return; if (ghostObject != null) ghostObject.transform.position = eventData.position; } public void OnEndDrag(PointerEventData eventData) { if (draggedSlot != this) return; // Hide ghost if (ghostObject != null) ghostObject.SetActive(false); // Restore icon opacity Color c = iconImage.color; c.a = 1f; iconImage.color = c; draggedSlot = null; } public void OnDrop(PointerEventData eventData) { if (draggedSlot == null || draggedSlot == this) return; onSwapRequested?.Invoke(draggedSlot.SlotIndex, slotIndex); } public void OnPointerClick(PointerEventData eventData) { if (eventData.dragging) return; if (eventData.button == PointerEventData.InputButton.Right) onClicked?.Invoke(slotIndex, true); } } }