Traveller/scripts/inventory/UniversalItemContainer.cs
Cold-Mint b9a9349596
Fixed an issue where two items would be selected at the same time when the player picked up the item and re-selected it.
修复玩家捡起物品后,重新选中物品,会有两个物品同时被选择的问题。
2024-09-25 21:56:54 +08:00

380 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
}
}