using System; using System.Collections.Generic; using OTGIntegrated.Core; using OTGIntegrated.Items; using UnityEngine; namespace OTGIntegrated.Inventory { public sealed class Inventory : MonoBehaviour { [SerializeField] private int maxSlots = 20; [SerializeField] private List slots = new List(); public int MaxSlots => maxSlots; public event Action OnInventoryChanged; public bool AddItem(ItemData item, int amount) { if (!CanAddItem(item, amount)) { GameEvents.RaiseNotification("Недостаточно места в инвентаре"); return false; } AddItemInternal(item, amount); RaiseChanged(); GameEvents.RaiseItemAdded(item, amount); GameEvents.RaiseNotification($"+{amount} {item.displayName}"); return true; } public bool RemoveItem(ItemData item, int amount) { if (item == null || amount <= 0 || !HasItem(item, amount)) { return false; } int remaining = amount; for (int i = slots.Count - 1; i >= 0 && remaining > 0; i--) { InventorySlot slot = slots[i]; if (slot == null || slot.item != item || slot.amount <= 0) { continue; } int removed = Mathf.Min(slot.amount, remaining); slot.amount -= removed; remaining -= removed; if (slot.IsEmpty()) { slots.RemoveAt(i); } } RaiseChanged(); GameEvents.RaiseItemRemoved(item, amount); return true; } public bool HasItem(ItemData item, int amount) { return item != null && amount > 0 && GetAmount(item) >= amount; } public int GetAmount(ItemData item) { if (item == null) { return 0; } int total = 0; for (int i = 0; i < slots.Count; i++) { InventorySlot slot = slots[i]; if (slot != null && slot.item == item && slot.amount > 0) { total += slot.amount; } } return total; } public IReadOnlyList GetAllSlots() { return slots; } public void Clear() { slots.Clear(); RaiseChanged(); } public bool CanAddItem(ItemData item, int amount) { if (item == null || amount <= 0) { return false; } int remaining = amount; int stackSize = Mathf.Max(1, item.maxStack); for (int i = 0; i < slots.Count; i++) { InventorySlot slot = slots[i]; if (slot != null && slot.item == item && slot.amount > 0 && slot.amount < stackSize) { remaining -= stackSize - slot.amount; if (remaining <= 0) { return true; } } } int freeSlots = Mathf.Max(0, maxSlots - slots.Count); int requiredSlots = Mathf.CeilToInt(remaining / (float)stackSize); return requiredSlots <= freeSlots; } public List SaveData() { List saveSlots = new List(); for (int i = 0; i < slots.Count; i++) { InventorySlot slot = slots[i]; if (slot == null || slot.IsEmpty()) { continue; } saveSlots.Add(new InventorySlotSaveData { itemId = slot.item.itemId, amount = slot.amount }); } return saveSlots; } public void LoadData(List saveSlots, IEnumerable itemCatalog) { slots.Clear(); Dictionary lookup = new Dictionary(StringComparer.Ordinal); if (itemCatalog != null) { foreach (ItemData item in itemCatalog) { if (item != null && !string.IsNullOrWhiteSpace(item.itemId)) { lookup[item.itemId] = item; } } } if (saveSlots != null) { for (int i = 0; i < saveSlots.Count; i++) { InventorySlotSaveData slot = saveSlots[i]; if (slot == null || string.IsNullOrWhiteSpace(slot.itemId) || slot.amount <= 0) { continue; } if (lookup.TryGetValue(slot.itemId, out ItemData item)) { AddItemInternal(item, slot.amount); } } } RaiseChanged(); } private void AddItemInternal(ItemData item, int amount) { int remaining = amount; int stackSize = Mathf.Max(1, item.maxStack); for (int i = 0; i < slots.Count && remaining > 0; i++) { InventorySlot slot = slots[i]; if (slot == null || slot.item != item || slot.amount >= stackSize) { continue; } int added = Mathf.Min(stackSize - slot.amount, remaining); slot.amount += added; remaining -= added; } while (remaining > 0 && slots.Count < maxSlots) { int added = Mathf.Min(stackSize, remaining); slots.Add(new InventorySlot(item, added)); remaining -= added; } } private void RaiseChanged() { OnInventoryChanged?.Invoke(); } } }