295 lines
6.8 KiB
C#
295 lines
6.8 KiB
C#
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<Item>, 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<T> where T : IEquatable<Item>, ICountable
|
|
{
|
|
int Capacity { get; }
|
|
int Count { get; }
|
|
|
|
IResult<Unit, InventoryError> Add(Item item, int count = 1);
|
|
IResult<Unit, InventoryError> Remove(Item item, int count = 1);
|
|
IEnumerable<T> Find(Func<T, bool> predicate);
|
|
bool Has(Item item);
|
|
}
|
|
|
|
[CreateAssetMenu(menuName = KitsuneCafeMenu.Item + "Inventory")]
|
|
public class Inventory : ScriptableObject, IList, IInventory<InventoryItem>, IEnumerable<InventoryItem>
|
|
{
|
|
[SerializeField]
|
|
private List<InventoryItem> 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<InventoryItem> Find(Func<InventoryItem, bool> predicate)
|
|
{
|
|
for (int i = 0; i < Count; i++)
|
|
{
|
|
var item = items[i];
|
|
|
|
if (predicate(item))
|
|
{
|
|
yield return item;
|
|
}
|
|
}
|
|
}
|
|
|
|
private IResult<int, InventoryError> AddToExisting(Item item, int count)
|
|
{
|
|
if (count <= 0)
|
|
{
|
|
return Result.Err<int, InventoryError>(InventoryError.InvalidQuantity);
|
|
}
|
|
|
|
for (int i = 0; i < Count; i++)
|
|
{
|
|
var existingItem = items[i];
|
|
|
|
if (existingItem.Equals(item))
|
|
{
|
|
if (!item.IsStackable)
|
|
{
|
|
return Result.Err<int, InventoryError>(InventoryError.ItemNotStackable);
|
|
}
|
|
|
|
var consumedAmount = existingItem.IncreaseCount(count, out var updatedItem);
|
|
items[i] = updatedItem.Value;
|
|
|
|
count -= consumedAmount;
|
|
if (count <= 0)
|
|
{
|
|
return Result.Ok<int, InventoryError>(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Result.Ok<int, InventoryError>(count);
|
|
}
|
|
|
|
private IResult<Unit, InventoryError> AddNew(Item item, int count)
|
|
{
|
|
while (count > 0)
|
|
{
|
|
if (IsFull)
|
|
{
|
|
return Result.Err<Unit, InventoryError>(InventoryError.InventoryFull);
|
|
}
|
|
|
|
count -= InventoryItem.Create(item, count, out var newItem);
|
|
items.Add(newItem.Value);
|
|
|
|
}
|
|
|
|
return Result.Ok<Unit, InventoryError>(default);
|
|
}
|
|
|
|
public IResult<Unit, InventoryError> Add(Item item, int count = 1)
|
|
{
|
|
return AddToExisting(item, count)
|
|
.Where(count => count > 0)
|
|
.SelectMany(count => AddNew(item, count)) switch
|
|
{
|
|
Ok<Unit, InventoryError> ok => ok,
|
|
Err<Unit, InventoryError>(InventoryError.None) => Result.Ok<Unit, InventoryError>(default),
|
|
var err => err
|
|
};
|
|
}
|
|
|
|
public IResult<Unit, InventoryError> Remove(Item item, int count = 1)
|
|
{
|
|
if (count <= 0)
|
|
{
|
|
return Result.Err<Unit, InventoryError>(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, InventoryError>(Unit.Default);
|
|
}
|
|
else
|
|
{
|
|
count -= existingItem.Count;
|
|
items.RemoveAt(i);
|
|
|
|
if (count == 0)
|
|
{
|
|
return Result.Ok<Unit, InventoryError>(Unit.Default);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (count > 0)
|
|
{
|
|
return Result.Err<Unit, InventoryError>(InventoryError.NotEnoughQuantity);
|
|
}
|
|
|
|
return Result.Ok<Unit, InventoryError>(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<InventoryItem> 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);
|
|
}
|
|
}
|
|
}
|