using System; using System.Collections; using System.Collections.Generic; using KitsuneCafe.Sys; using ObservableCollections; using UnityEngine; using Unit = KitsuneCafe.Sys.Unit; namespace KitsuneCafe.ItemSystem { [Serializable] public struct InventoryItem : IEquatable, ICountable { public Item Item; [SerializeField] private int count; public readonly int Count => count; private InventoryItem(Item item, int count) { Item = item; this.count = count; } public static int Create(Item item, int count, out InventoryItem? inventoryItem) { int quantity = Math.Clamp(count, 0, item.MaxStackCount); inventoryItem = quantity > 0 ? new InventoryItem(item, quantity) : null; return quantity; } public readonly int IncreaseCount(int count, out InventoryItem? item) { return Create(Item, Count + count, out item); } public readonly int ReduceCount(int count, out InventoryItem? item) { return Create(Item, Count - count, out item); } public readonly bool Equals(Item other) { return Item == other; } public override readonly bool Equals(object obj) { return obj is InventoryItem other && Item == other.Item && Count == other.Count; } public override int GetHashCode() { return HashCode.Combine(Item, Count); } } public enum InventoryError { None, InventoryFull, ItemNotFound, NotEnoughQuantity, ItemNotStackable, InvalidQuantity } public interface ICountable { int Count { get; } } public interface IInventory where T : IEquatable, ICountable { int Capacity { get; } int Count { get; } IResult Add(Item item, int count = 1); IResult Remove(Item item, int count = 1); IEnumerable Find(Func predicate); bool Has(Item item); } [CreateAssetMenu(menuName = KitsuneCafeMenu.Item + "Inventory")] public class Inventory : ScriptableObject, IList, IInventory, IEnumerable { [SerializeField] private List items = new(); [SerializeField] private int capacity = 8; public int Capacity => capacity; public int Count => items.Count; public bool IsEmpty => Count == 0; public bool IsFull => Count == Capacity; public object SyncRoot => ((IList)items).SyncRoot; public bool IsFixedSize => ((IList)items).IsFixedSize; public bool IsReadOnly => ((IList)items).IsReadOnly; public bool IsSynchronized => ((ICollection)items).IsSynchronized; object IList.this[int index] { get => items[index]; set => items[index] = (InventoryItem)value; } public InventoryItem this[int index] { get => items[index]; set => items[index] = value; } public IEnumerable Find(Func predicate) { for (int i = 0; i < Count; i++) { var item = items[i]; if (predicate(item)) { yield return item; } } } private IResult AddToExisting(Item item, int count) { if (count <= 0) { return Result.Err(InventoryError.InvalidQuantity); } for (int i = 0; i < Count; i++) { var existingItem = items[i]; if (existingItem.Equals(item)) { if (!item.IsStackable) { return Result.Err(InventoryError.ItemNotStackable); } var consumedAmount = existingItem.IncreaseCount(count, out var updatedItem); items[i] = updatedItem.Value; count -= consumedAmount; if (count <= 0) { return Result.Ok(0); } } } return Result.Ok(count); } private IResult AddNew(Item item, int count) { while (count > 0) { if (IsFull) { return Result.Err(InventoryError.InventoryFull); } count -= InventoryItem.Create(item, count, out var newItem); items.Add(newItem.Value); } return Result.Ok(default); } public IResult Add(Item item, int count = 1) { return AddToExisting(item, count) .Where(count => count > 0) .SelectMany(count => AddNew(item, count)) switch { Ok ok => ok, Err(InventoryError.None) => Result.Ok(default), var err => err }; } public IResult Remove(Item item, int count = 1) { if (count <= 0) { return Result.Err(InventoryError.InvalidQuantity); } for (int i = items.Count - 1; i >= 0; i--) { var existingItem = items[i]; if (existingItem.Item == item) { if (existingItem.Count > count) { existingItem.ReduceCount(count, out var updatedItem); items[i] = updatedItem.Value; return Result.Ok(Unit.Default); } else { count -= existingItem.Count; items.RemoveAt(i); if (count == 0) { return Result.Ok(Unit.Default); } } } } if (count > 0) { return Result.Err(InventoryError.NotEnoughQuantity); } return Result.Ok(Unit.Default); } public bool Has(Item item) { for (int i = 0; i < Count; i++) { if (items[i].Equals(item)) { return true; } } return false; } public IEnumerator GetEnumerator() { return items.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public int Add(object value) { return ((IList)items).Add(value); } public void Clear() { ((IList)items).Clear(); } public bool Contains(object value) { return ((IList)items).Contains(value); } public int IndexOf(object value) { return ((IList)items).IndexOf(value); } public void Insert(int index, object value) { ((IList)items).Insert(index, value); } public void Remove(object value) { ((IList)items).Remove(value); } public void RemoveAt(int index) { ((IList)items).RemoveAt(index); } public void CopyTo(Array array, int index) { ((ICollection)items).CopyTo(array, index); } } }