using System; using System.Collections.Generic; using System.Linq; using UnityEngine; [Serializable] public class Inventory { [SerializeField] private List<InventorySlot> items; private GameObject gameObject; public GameObject GameObject => gameObject; // The number of slots that have an item inside of item public int Count => items.Count; //The capacity inside of it public int Capacity => items.Capacity; // Event triggered when inventory changes public event Action<int> OnSlotChanged; public event Action<int, int> OnSlotsChanged; //Constructor public Inventory(int capacity, GameObject gameObject) { items = new List<InventorySlot>(capacity); for (int i = 0; i < capacity; i++) { InventorySlot slot = new InventorySlot(this); items.Add(slot); } this.gameObject = gameObject; } //Adds an item to the inventory public bool AddItem(Item item, int amount = 1) { // Find the first empty slot or slot with the same item type InventorySlot existentSlot = FindAvailableSlotByItem(item); if (existentSlot != null && !existentSlot.IsSlotFull()) { // Increment existing item amount if possible int possibleAmountToAdd = Math.Min(amount, existentSlot.MaxAmount - existentSlot.Amount); existentSlot.Amount += possibleAmountToAdd; amount -= possibleAmountToAdd; if (amount <= 0) { OnSlotChanged?.Invoke(existentSlot.SlotIndex); return true; } } for (int i = 0; i < items.Count; i++) { if (items[i] == null || items[i].IsEmpty()) { items[i].AddItemSlot(item, amount); OnSlotChanged?.Invoke(items[i].SlotIndex); return true; } } Debug.LogWarning("Inventory is full!"); return false; } //Removes an item from a slot public void RemoveItem(int index) { if (index >= 0 && index < items.Count && items[index] != null) { items[index] = new InventorySlot(this); // Replace with an empty OnSlotChanged?.Invoke(index); } else { Debug.LogWarning("Invalid index or slot is already empty."); } } public void RemoveItemAmount(int index, int amount) { if (items[index] != null) { int targetAmountLeft = items[index].Amount - amount; if (targetAmountLeft == 0) { items.RemoveAt(index); } else { items[index].Amount -= amount; } OnSlotChanged?.Invoke(items[index].SlotIndex); } } public static bool TryMergeSlotsInventoriesSlots(InventorySlot slotA, InventorySlot slotB) { if (slotA.Item.Equals(slotB.Item)) { int mergeResult = slotA.Amount + slotB.Amount; if (mergeResult <= slotB.MaxAmount) { slotB.AddItemSlot(slotA.Item, slotA.Amount + slotB.Amount); slotA.Clear(); return true; } return false; } return false; } public static void SwapInventoriesSlot(InventorySlot slotA, InventorySlot slotB) { // Validate inputs if (slotA == null || slotB == null || slotA.Owner == null || slotB.Owner == null) { Debug.LogError("Invalid slots or owners for swapping!"); return; } // Store original ownership Inventory ownerA = slotA.Owner; Inventory ownerB = slotB.Owner; // Store temporary data for swapping Item tempItem = slotA.Item; int tempAmount = slotA.Amount; // Swap content slotA.AddItemSlot(slotB.Item, slotB.Amount); slotB.AddItemSlot(tempItem, tempAmount); // Notify both inventories about the change ownerA.OnSlotChanged?.Invoke(slotA.SlotIndex); ownerB.OnSlotChanged?.Invoke(slotB.SlotIndex); //Debug.Log($"Swapped items between Inventory A (Slot {slotA.SlotIndex}) and Inventory B (Slot {slotB.SlotIndex})"); } public int GetSlotIndex(InventorySlot inventorySlot) { return items.FindIndex(0, slot => slot == inventorySlot); } //Swaps items to another slot public void SwapItemSlot(int index, int swapIndex) { // Perform the swap InventorySlot temp = items[index]; items[index] = items[swapIndex]; items[swapIndex] = temp; Debug.Log($"Perform swap between index {index} and index {swapIndex}"); // Notify observers of the change OnSlotsChanged?.Invoke(index, swapIndex); } public void SplitSlot(int index) { if (!IsFull()) { if (CanSplitItemSlot(index)) { InventorySlot newSlot = FindEmptySlot(); if (newSlot == null) return; InventorySlot s = GetSlot(index); int amountToTransfer = CalculateStackDivision(s.Amount); int leftOver = s.Amount - amountToTransfer; s.Amount = amountToTransfer; s.AddItemSlot(s.Item, amountToTransfer); newSlot.AddItemSlot(s.Item, leftOver); OnSlotsChanged?.Invoke(s.SlotIndex, newSlot.SlotIndex); return; } Debug.Log("Slot is not stackable or doesn't have enough items to split the stack"); return; } Debug.Log("Inventory is Full"); } //Check if the item is stackable //Returns true if the item is stackable //And also returns the slot of the item private bool CanSplitItemSlot(int index) { InventorySlot slot = items[index]; return CanSplitItemSlot(slot); } private bool CanSplitItemSlot(InventorySlot slot) { if(IsFull()) return false; //Tries to find a slot that contains an empty slot InventorySlot avalableSlot = FindEmptySlot(); return avalableSlot != null && slot.Item.IsStackable && slot.Amount > 1; } //Gets an item public Item GetItem(int index) => items[index].Item; public InventorySlot InventorySlot(int index) => items[index]; //Calculates the division of the stack private int CalculateStackDivision(int amount) => Mathf.CeilToInt(amount / 2f); //Checks if a slot exists with same item private InventorySlot FindAvailableSlotByItem(Item item) { foreach (InventorySlot slot in items) { if (slot.Item != null && slot.Item.Equals(item) && !slot.IsSlotFull()) { return slot; } } return null; } public bool TryMergeSlots(InventorySlot slotA, InventorySlot slotB) { if (TryMergeSlotsInventoriesSlots(slotA, slotB)) { OnSlotsChanged.Invoke(slotA.SlotIndex, slotB.SlotIndex); return true; } return false; } //Finds an empty slot private InventorySlot FindEmptySlot() => GetSlot(items.FindIndex(x => x.IsEmpty())); //public void DropItem(int index, int amount) //{ // Item item = GetItem(index); // GameObject go = gameObject.itemStack; // if (go != null) // { // Vector3 targePos = new Vector3(gameObject.transform.position.x, gameObject.transform.position.y, 0); // GameObject obj = UnityEngine.Object.Instantiate(go, targePos, Quaternion.identity); // ItemStack stack = obj.GetComponent<ItemStack>(); // stack.ItemStackInit(item, amount); // RemoveItem(index, amount); // return; // } // else // { // Debug.LogError($"Item stack is Null"); // } //} public InventorySlot GetSlot(int index) { if (items[index] == null) return null; return items[index]; } //Checks if the inventory is full public bool IsFull() { return items.All(slot => slot != null && !slot.IsEmpty() && slot.IsSlotFull()); } } [Serializable] public class InventorySlot { //Variables [SerializeField] private Item item; [SerializeField] private int amount; [SerializeField] private int maxAmount; //Testing purposes [SerializeField] private string ownerName; [SerializeField] private int slotIndex; [NonSerialized] private Inventory owner; public Inventory Owner { get => owner; set => owner = value; } public int SlotIndex => owner.GetSlotIndex(this); //Properties public Item Item => item; public int MaxAmount => maxAmount; public int Amount { get => amount; set => amount = value; } public void AddItemSlot(Item item = null, int amount = 0) { this.item = item; this.amount = amount; if (item != null) { maxAmount = item.StackAmount; } } //Constructor public InventorySlot(Inventory owner) { ownerName = owner.ToString(); this.owner = owner; } //Checks id the slot is full public bool IsSlotFull() => amount == maxAmount; public void Clear() { item = null; amount = 0; maxAmount = 0; } public bool IsEmpty() => item == null; }