286 lines
7.8 KiB
C#
286 lines
7.8 KiB
C#
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
using TMPro;
|
||
|
||
/// <summary>
|
||
/// Слот инвентаря - отображает предмет и его количество
|
||
/// </summary>
|
||
public class Slot : MonoBehaviour
|
||
{
|
||
public int slotIndex;
|
||
public ItemData item;
|
||
public int amount;
|
||
|
||
[Header("UI References")]
|
||
public Image iconImage;
|
||
public TextMeshProUGUI amountText;
|
||
public GameObject amountPanel;
|
||
public Image slotBackground;
|
||
|
||
[Header("Visual Settings")]
|
||
[SerializeField] private Color emptySlotColor = new Color(0.2f, 0.2f, 0.2f, 0.5f);
|
||
[SerializeField] private Color filledSlotColor = new Color(0.3f, 0.3f, 0.3f, 0.9f);
|
||
[SerializeField] private Color hoverColor = new Color(0.5f, 0.5f, 0.5f, 1f);
|
||
[SerializeField] private Color selectedColor = new Color(0.4f, 0.6f, 0.9f, 1f);
|
||
|
||
[Header("Drag & Drop")]
|
||
[SerializeField] private bool allowDrag = true;
|
||
|
||
public bool IsEmpty => item == null || amount <= 0;
|
||
public bool HasItem => !IsEmpty;
|
||
|
||
// События для внешних подписчиков
|
||
public System.Action<Slot> OnSlotClicked;
|
||
public System.Action<Slot> OnSlotRightClicked;
|
||
public System.Action<Slot> OnSlotHovered;
|
||
public System.Action<Slot> OnSlotDragStart;
|
||
|
||
private bool _isSelected;
|
||
|
||
void Awake()
|
||
{
|
||
// Автоматическое получение компонентов если не назначены
|
||
if (iconImage == null)
|
||
iconImage = transform.Find("Icon")?.GetComponent<Image>();
|
||
|
||
if (amountText == null)
|
||
amountText = transform.Find("AmountPanel/Text")?.GetComponent<TextMeshProUGUI>();
|
||
|
||
if (amountPanel == null)
|
||
amountPanel = transform.Find("AmountPanel")?.gameObject;
|
||
|
||
if (slotBackground == null)
|
||
slotBackground = GetComponent<Image>();
|
||
}
|
||
|
||
void Start()
|
||
{
|
||
UpdateSlotUI();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Обновить визуальное отображение слота
|
||
/// </summary>
|
||
public void UpdateSlotUI()
|
||
{
|
||
if (IsEmpty)
|
||
{
|
||
ClearVisuals();
|
||
return;
|
||
}
|
||
|
||
// Иконка
|
||
if (item.icon != null)
|
||
{
|
||
iconImage.sprite = item.icon;
|
||
iconImage.enabled = true;
|
||
}
|
||
else
|
||
{
|
||
iconImage.enabled = false;
|
||
}
|
||
|
||
// Количество
|
||
if (amount > 1)
|
||
{
|
||
amountText.text = amount.ToString();
|
||
amountPanel.SetActive(true);
|
||
}
|
||
else
|
||
{
|
||
amountPanel.SetActive(false);
|
||
}
|
||
|
||
// Цвет фона
|
||
if (slotBackground != null)
|
||
slotBackground.color = filledSlotColor;
|
||
}
|
||
|
||
private void ClearVisuals()
|
||
{
|
||
iconImage.sprite = null;
|
||
iconImage.enabled = false;
|
||
amountPanel?.SetActive(false);
|
||
|
||
if (slotBackground != null)
|
||
slotBackground.color = emptySlotColor;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Очистить слот
|
||
/// </summary>
|
||
public void Clear()
|
||
{
|
||
item = null;
|
||
amount = 0;
|
||
UpdateSlotUI();
|
||
|
||
// Уведомляем менеджер
|
||
if (InventoryManager.Instance != null)
|
||
{
|
||
InventoryManager.Instance.OnSlotCleared(slotIndex);
|
||
}
|
||
|
||
// Вызываем событие
|
||
InventoryEvents.Instance?.InvokeSlotChanged(slotIndex, null);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Установить предмет в слот
|
||
/// </summary>
|
||
public void SetItem(ItemData newItem, int newAmount)
|
||
{
|
||
item = newItem;
|
||
amount = newAmount;
|
||
UpdateSlotUI();
|
||
|
||
// Вызываем событие изменения
|
||
InventoryEvents.Instance?.InvokeSlotChanged(slotIndex, item?.id);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Добавить предметы в слот
|
||
/// </summary>
|
||
/// <returns>Количество не поместившихся предметов</returns>
|
||
public int AddItems(ItemData newItem, int addAmount)
|
||
{
|
||
if (item == null)
|
||
{
|
||
// Пустой слот - просто кладём
|
||
item = newItem;
|
||
amount = Mathf.Min(addAmount, newItem.maxStack);
|
||
UpdateSlotUI();
|
||
|
||
// Событие добавления
|
||
InventoryEvents.Instance?.InvokeItemAdded(item.id, amount);
|
||
InventoryEvents.Instance?.InvokeSlotChanged(slotIndex, item.id);
|
||
|
||
return addAmount - amount;
|
||
}
|
||
|
||
if (item.id != newItem.id)
|
||
{
|
||
// Другой предмет - нельзя добавить
|
||
return addAmount;
|
||
}
|
||
|
||
// Тот же предмет - пытаемся добавить в стек
|
||
int space = item.maxStack - amount;
|
||
if (space <= 0) return addAmount;
|
||
|
||
int toAdd = Mathf.Min(space, addAmount);
|
||
amount += toAdd;
|
||
UpdateSlotUI();
|
||
|
||
// Событие изменения
|
||
InventoryEvents.Instance?.InvokeItemCountChanged(item.id, amount);
|
||
InventoryEvents.Instance?.InvokeSlotChanged(slotIndex, item.id);
|
||
|
||
return addAmount - toAdd;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Взять часть предметов из слота
|
||
/// </summary>
|
||
/// <returns>(предмет, взятое количество)</returns>
|
||
public (ItemData, int) TakeItems(int takeAmount)
|
||
{
|
||
if (IsEmpty) return (null, 0);
|
||
|
||
int actualTake = Mathf.Min(takeAmount, amount);
|
||
amount -= actualTake;
|
||
|
||
ItemData resultItem = item;
|
||
|
||
if (amount <= 0)
|
||
{
|
||
Clear();
|
||
}
|
||
else
|
||
{
|
||
UpdateSlotUI();
|
||
// Событие изменения количества
|
||
InventoryEvents.Instance?.InvokeItemCountChanged(item.id, amount);
|
||
}
|
||
|
||
// Событие удаления
|
||
InventoryEvents.Instance?.InvokeItemRemoved(resultItem.id, actualTake);
|
||
|
||
return (resultItem, actualTake);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Взять все предметы из слота
|
||
/// </summary>
|
||
public (ItemData, int) TakeAll()
|
||
{
|
||
return TakeItems(amount);
|
||
}
|
||
|
||
// ==================== UI Events ====================
|
||
|
||
void OnMouseEnter()
|
||
{
|
||
if (slotBackground != null && !_isSelected)
|
||
slotBackground.color = hoverColor;
|
||
|
||
OnSlotHovered?.Invoke(this);
|
||
}
|
||
|
||
void OnMouseExit()
|
||
{
|
||
if (slotBackground != null && !_isSelected)
|
||
slotBackground.color = IsEmpty ? emptySlotColor : filledSlotColor;
|
||
}
|
||
|
||
void OnMouseDown()
|
||
{
|
||
if (Input.GetMouseButton(0))
|
||
{
|
||
OnSlotClicked?.Invoke(this);
|
||
|
||
if (allowDrag)
|
||
{
|
||
OnSlotDragStart?.Invoke(this);
|
||
}
|
||
}
|
||
else if (Input.GetMouseButton(1))
|
||
{
|
||
OnSlotRightClicked?.Invoke(this);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Установить состояние выделения слота
|
||
/// </summary>
|
||
public void SetSelected(bool selected)
|
||
{
|
||
_isSelected = selected;
|
||
if (slotBackground != null)
|
||
{
|
||
slotBackground.color = selected ? selectedColor :
|
||
(IsEmpty ? emptySlotColor : filledSlotColor);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Поменять содержимое с другим слотом
|
||
/// </summary>
|
||
public void SwapWith(Slot other)
|
||
{
|
||
if (other == null || other == this) return;
|
||
|
||
var tempItem = this.item;
|
||
var tempAmount = this.amount;
|
||
|
||
this.SetItem(other.item, other.amount);
|
||
other.SetItem(tempItem, tempAmount);
|
||
}
|
||
|
||
// ==================== Getters ====================
|
||
|
||
public ItemData GetItem() => item;
|
||
public int GetAmount() => amount;
|
||
public string GetItemId() => item?.id;
|
||
public bool IsSelected() => _isSelected;
|
||
} |