Newer
Older
TheVengeance-Project-IADE-Unity2D / Assets / Scripts / Player / Inventory / Inventory.cs
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;
}