380 lines
10 KiB
C#
380 lines
10 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using ColdMint.scripts.map.events;
|
||
using JetBrains.Annotations;
|
||
|
||
namespace ColdMint.scripts.inventory;
|
||
|
||
/// <summary>
|
||
/// <para>UniversalItemContainer</para>
|
||
/// <para>通用的物品容器</para>
|
||
/// </summary>
|
||
public class UniversalItemContainer(int totalCapacity) : IItemContainer
|
||
{
|
||
private readonly List<IItem> _itemList = [];
|
||
|
||
/// <summary>
|
||
/// <para>UnknownIndex</para>
|
||
/// <para>未知位置</para>
|
||
/// </summary>
|
||
private const int UnknownIndex = -1;
|
||
|
||
//_selectIndex defaults to 0.
|
||
//_selectIndex默认为0.
|
||
private int _selectIndex;
|
||
|
||
[MustDisposeResource]
|
||
public IEnumerator<IItem> GetEnumerator()
|
||
{
|
||
return _itemList.GetEnumerator();
|
||
}
|
||
|
||
[MustDisposeResource]
|
||
IEnumerator IEnumerable.GetEnumerator()
|
||
{
|
||
return GetEnumerator();
|
||
}
|
||
|
||
public Action<SelectedItemChangeEvent>? SelectedItemChangeEvent { get; set; }
|
||
public Action<ItemDataChangeEvent>? ItemDataChangeEvent { get; set; }
|
||
|
||
public bool CanAddItem(IItem item)
|
||
{
|
||
//If the capacity is not full, directly return to add items
|
||
//如果未占满容量,直接返回可添加物品
|
||
if (GetUsedCapacity() < totalCapacity)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
if (item.MaxQuantity == 1)
|
||
{
|
||
//New items do not support overlay, capacity is full, return cannot add.
|
||
//新物品不支持叠加,容量已满,返回不能添加。
|
||
return false;
|
||
}
|
||
|
||
//If the capacity is full, we calculate whether we can spread the new items evenly among the existing items.
|
||
//如果容量占满了,我们计算是否能将新物品均摊在已有的物品内。
|
||
var unallocatedQuantity = item.Quantity;
|
||
foreach (var unitItem in _itemList)
|
||
{
|
||
var number = unitItem.MergeableItemCount(item, unallocatedQuantity);
|
||
if (number == 0)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
unallocatedQuantity -= number;
|
||
if (unallocatedQuantity < 1)
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return unallocatedQuantity < 1;
|
||
}
|
||
|
||
private void UpdateSelectStatus(int index, IItem item)
|
||
{
|
||
item.IsSelect = index == _selectIndex;
|
||
}
|
||
|
||
public int AddItem(IItem item)
|
||
{
|
||
if (item.MaxQuantity == 1)
|
||
{
|
||
if (GetUsedCapacity() >= totalCapacity)
|
||
{
|
||
//Items cannot be stacked and cannot be added if the capacity is full.
|
||
//物品不能叠加,且容量已满,则无法添加。
|
||
return 0;
|
||
}
|
||
|
||
_itemList.Add(item);
|
||
UpdateSelectStatus(_itemList.Count - 1, item);
|
||
ItemDataChangeEvent?.Invoke(new ItemDataChangeEvent
|
||
{
|
||
NewItem = item,
|
||
NewIndex = _itemList.Count - 1,
|
||
Type = Config.ItemDataChangeEventType.QuantityAdded
|
||
});
|
||
return item.Quantity;
|
||
}
|
||
|
||
//There can be more than one item, try to share equally.
|
||
//物品可有多个,尝试均摊。
|
||
var originalQuantity = item.Quantity;
|
||
var index = 0;
|
||
foreach (var unitItem in _itemList)
|
||
{
|
||
var number = unitItem.MergeableItemCount(item, item.Quantity);
|
||
if (number == 0)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
item.Quantity -= number;
|
||
unitItem.Quantity += number;
|
||
ItemDataChangeEvent?.Invoke(new ItemDataChangeEvent
|
||
{
|
||
NewItem = unitItem,
|
||
NewIndex = index,
|
||
Type = Config.ItemDataChangeEventType.QuantityAdded
|
||
});
|
||
if (item.Quantity < 1)
|
||
{
|
||
//New items are fully shared.
|
||
//新物品完全被均摊。
|
||
return originalQuantity;
|
||
}
|
||
|
||
index++;
|
||
}
|
||
|
||
//New items have some left over.
|
||
//新物品有一些剩余。
|
||
if (GetUsedCapacity() >= totalCapacity)
|
||
{
|
||
//The capacity is full. The remaining capacity cannot be stored.
|
||
//容量已满,无法存放剩余。
|
||
return originalQuantity - item.Quantity;
|
||
}
|
||
|
||
//Add the rest to the container.
|
||
//添加剩余到容器内。
|
||
_itemList.Add(item);
|
||
UpdateSelectStatus(_itemList.Count - 1, item);
|
||
ItemDataChangeEvent?.Invoke(new ItemDataChangeEvent
|
||
{
|
||
NewItem = item,
|
||
NewIndex = _itemList.Count - 1,
|
||
Type = Config.ItemDataChangeEventType.Add
|
||
});
|
||
return originalQuantity;
|
||
}
|
||
|
||
public bool SupportSelect { get; set; }
|
||
public bool EnablePlaceholder { get; set; }
|
||
|
||
public IItem? GetPlaceHolderItem()
|
||
{
|
||
return EnablePlaceholder ? new PlaceholderItem() : null;
|
||
}
|
||
|
||
public int GetSelectIndex()
|
||
{
|
||
return _selectIndex;
|
||
}
|
||
|
||
public IItem? GetSelectItem()
|
||
{
|
||
var count = _itemList.Count;
|
||
if (count == 0)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
return _selectIndex < count ? _itemList[_selectIndex] : null;
|
||
}
|
||
|
||
public IItem? GetItem(int index)
|
||
{
|
||
return GetValidIndex(index) == UnknownIndex ? null : _itemList[index];
|
||
}
|
||
|
||
/// <summary>
|
||
/// <para>Get valid index</para>
|
||
/// <para>获取有效的索引</para>
|
||
/// </summary>
|
||
/// <param name="index"></param>
|
||
/// <returns>
|
||
///<para>Return -1 if the given index exceeds the valid range of the list, otherwise return the given index itself.</para>
|
||
///<para>如果给定的索引超过了列表的有效范围,那么返回-1,否则返回给定的索引本身。</para>
|
||
/// </returns>
|
||
private int GetValidIndex(int index)
|
||
{
|
||
var count = _itemList.Count;
|
||
if (count == 0)
|
||
{
|
||
return UnknownIndex;
|
||
}
|
||
if (index >= count || index < 0)
|
||
{
|
||
return UnknownIndex;
|
||
}
|
||
return index;
|
||
}
|
||
|
||
/// <summary>
|
||
/// <para>Gets a normalized subscript index</para>
|
||
/// <para>获取规范化的下标索引</para>
|
||
/// </summary>
|
||
/// <param name="index"></param>
|
||
/// <returns>
|
||
/// <para>The difference between this method and <see cref="GetValidIndex"/> is that if the given index is out of range, the result will be returned after rounding.</para>
|
||
/// <para>失败返回-1,成功返回不会导致下标越界的索引,此方法和<see cref="GetValidIndex"/>区别是,如果给定的索引超出了范围,那么会将结果取余后返回。</para>
|
||
/// </returns>
|
||
private int GetNormalizeIndex(int index)
|
||
{
|
||
var count = _itemList.Count;
|
||
if (count == 0 || index < 0)
|
||
{
|
||
//Prevents the dividend from being 0
|
||
//防止被除数为0
|
||
return UnknownIndex;
|
||
}
|
||
|
||
return index % count;
|
||
}
|
||
|
||
|
||
public int RemoveSelectItem(int number)
|
||
{
|
||
return RemoveItem(_selectIndex, number);
|
||
}
|
||
|
||
public int RemoveItem(int itemIndex, int number)
|
||
{
|
||
if (number == 0)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
var index = GetValidIndex(itemIndex);
|
||
if (index == UnknownIndex)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
var item = _itemList[index];
|
||
var originalQuantity = item.Quantity;
|
||
if (number < 0)
|
||
{
|
||
//If the number entered is less than 0, all items are removed.
|
||
//输入的数量小于0,则移除全部物品。
|
||
item.Quantity = 0;
|
||
_itemList.RemoveAt(index);
|
||
return originalQuantity;
|
||
}
|
||
|
||
var removed = Math.Min(number, item.Quantity);
|
||
item.Quantity -= removed;
|
||
if (item.Quantity < 1)
|
||
{
|
||
_itemList.RemoveAt(index);
|
||
}
|
||
|
||
return removed;
|
||
}
|
||
|
||
public int GetUsedCapacity()
|
||
{
|
||
return _itemList.Count;
|
||
}
|
||
|
||
public int GetTotalCapacity()
|
||
{
|
||
return totalCapacity;
|
||
}
|
||
|
||
|
||
public void SelectNextItem()
|
||
{
|
||
var count = EnablePlaceholder ? totalCapacity : _itemList.Count;
|
||
if (count == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var oldSelectIndex = _selectIndex;
|
||
var newSelectIndex = _selectIndex + 1;
|
||
if (newSelectIndex >= count)
|
||
{
|
||
newSelectIndex = 0;
|
||
}
|
||
|
||
PrivateSelectItem(oldSelectIndex, newSelectIndex);
|
||
}
|
||
|
||
public void SelectPreviousItem()
|
||
{
|
||
var count = EnablePlaceholder ? totalCapacity : _itemList.Count;
|
||
if (count == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var oldSelectIndex = _selectIndex;
|
||
var newSelectIndex = _selectIndex - 1;
|
||
if (newSelectIndex < 0)
|
||
{
|
||
newSelectIndex = count - 1;
|
||
}
|
||
|
||
PrivateSelectItem(oldSelectIndex, newSelectIndex);
|
||
}
|
||
|
||
/// <summary>
|
||
/// <para>Private methods for selecting items</para>
|
||
/// <para>选择物品的私有方法</para>
|
||
/// </summary>
|
||
/// <param name="oldIndex"></param>
|
||
/// <param name="newIndex"></param>
|
||
private void PrivateSelectItem(int oldIndex, int newIndex)
|
||
{
|
||
if (!SupportSelect || oldIndex == newIndex)
|
||
{
|
||
return;
|
||
}
|
||
|
||
//There is no need to broadcast placeholders when an event is invoked.
|
||
//在调用事件时,无需广播占位符。
|
||
var oldItem = GetItem(oldIndex);
|
||
if (oldItem != null)
|
||
{
|
||
oldItem.IsSelect = false;
|
||
}
|
||
|
||
//There is no need to broadcast placeholders when an event is invoked.
|
||
//在调用事件时,无需广播占位符。
|
||
var newItem = GetItem(newIndex);
|
||
if (newItem != null)
|
||
{
|
||
newItem.IsSelect = true;
|
||
}
|
||
|
||
_selectIndex = newIndex;
|
||
SelectedItemChangeEvent?.Invoke(new SelectedItemChangeEvent
|
||
{
|
||
NewIndex = newIndex,
|
||
OldIndex = oldIndex,
|
||
NewItem = newItem,
|
||
OldItem = oldItem
|
||
});
|
||
}
|
||
|
||
|
||
public void SelectItem(int index)
|
||
{
|
||
if (EnablePlaceholder)
|
||
{
|
||
if (totalCapacity == 0)
|
||
{
|
||
return;
|
||
}
|
||
PrivateSelectItem(_selectIndex, index % totalCapacity);
|
||
}
|
||
else
|
||
{
|
||
var safeIndex = GetNormalizeIndex(index);
|
||
if (safeIndex == UnknownIndex)
|
||
{
|
||
return;
|
||
}
|
||
|
||
PrivateSelectItem(_selectIndex, safeIndex);
|
||
}
|
||
}
|
||
} |