Traveller/scripts/inventory/UniversalItemContainer.cs
2024-06-21 22:55:31 +08:00

298 lines
8.0 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 System.Linq;
using ColdMint.scripts.item;
using ColdMint.scripts.map.events;
using ColdMint.scripts.utils;
using Godot;
using JetBrains.Annotations;
namespace ColdMint.scripts.inventory;
/// <summary>
/// <para>UniversalItemContainer</para>
/// <para>通用的物品容器</para>
/// </summary>
public class UniversalItemContainer : IItemContainer
{
private readonly List<ItemSlotNode>? _itemSlotNodes = [];
private readonly PackedScene? _itemSlotPackedScene = GD.Load<PackedScene>("res://prefab/ui/ItemSlot.tscn");
/// <summary>
/// <para>UnknownIndex</para>
/// <para>未知位置</para>
/// </summary>
private const int UnknownIndex = -1;
//_selectIndex默认为0.
private int _selectIndex;
[MustDisposeResource]
public IEnumerator<ItemSlotNode> GetEnumerator()
{
return _itemSlotNodes?.GetEnumerator() ?? Enumerable.Empty<ItemSlotNode>().GetEnumerator();
}
[MustDisposeResource]
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public Action<SelectedItemSlotChangeEvent>? SelectedItemSlotChangeEvent { get; set; }
public bool CanAddItem(IItem item)
{
return Match(item) != null;
}
public bool AddItem(IItem item)
{
var itemSlotNode = Match(item);
if (itemSlotNode == null)
{
return false;
}
return itemSlotNode.AddItem(item);
}
public bool SupportSelect { get; set; }
public int GetSelectIndex()
{
return _selectIndex;
}
public ItemSlotNode? GetSelectItemSlotNode()
{
if (_itemSlotNodes == null || _itemSlotNodes.Count == 0)
{
return null;
}
if (_selectIndex < _itemSlotNodes.Count)
{
//Prevent subscripts from going out of bounds.
//防止下标越界。
return _itemSlotNodes[_selectIndex];
}
return null;
}
public int RemoveItemFromItemSlotBySelectIndex(int number) => RemoveItemFromItemSlot(_selectIndex, number);
public int GetItemSlotCount()
{
if (_itemSlotNodes == null)
{
return 0;
}
return _itemSlotNodes.Count;
}
public ItemSlotNode? GetItemSlotNode(int index)
{
if (_itemSlotNodes == null)
{
return null;
}
var safeIndex = GetSafeIndex(index);
return _itemSlotNodes[safeIndex];
}
public int RemoveItemFromItemSlot(int itemSlotIndex, int number)
{
if (_itemSlotNodes == null) return number;
var safeIndex = GetSafeIndex(itemSlotIndex);
if (safeIndex == UnknownIndex)
{
return number;
}
var itemSlot = _itemSlotNodes[safeIndex];
return itemSlot.RemoveItem(number);
}
/// <summary>
/// <para>Gets a secure subscript index</para>
/// <para>获取安全的下标索引</para>
/// </summary>
/// <param name="itemSlotIndex"></param>
/// <returns>
/// <para>-1 is returned on failure, and the index that does not result in an out-of-bounds subscript is returned on success</para>
/// <para>失败返回-1成功返回不会导致下标越界的索引</para>
/// </returns>
private int GetSafeIndex(int itemSlotIndex)
{
if (_itemSlotNodes == null)
{
return UnknownIndex;
}
var count = _itemSlotNodes.Count;
if (count == 0)
{
//Prevents the dividend from being 0
//防止被除数为0
return UnknownIndex;
}
return itemSlotIndex % count;
}
public ItemSlotNode? Match(IItem item)
{
//Find and return the first slot that can hold this item, if the list is null or not found, return null
//寻找并返回第一个遇到的可放置此物品的物品槽若列表为空或不存在将返回null
return _itemSlotNodes?.FirstOrDefault(itemSlotNode => itemSlotNode.CanAddItem(item));
}
public ItemSlotNode? AddItemSlot(Node rootNode)
{
if (_itemSlotNodes == null || _itemSlotPackedScene == null)
{
return null;
}
var itemSlotNode = NodeUtils.InstantiatePackedScene<ItemSlotNode>(_itemSlotPackedScene, rootNode);
if (itemSlotNode == null)
{
return null;
}
if (SupportSelect)
{
itemSlotNode.IsSelect = _itemSlotNodes.Count == _selectIndex;
}
else
{
itemSlotNode.IsSelect = false;
}
_itemSlotNodes.Add(itemSlotNode);
return itemSlotNode;
}
public void SelectTheNextItemSlot()
{
if (_itemSlotNodes == null)
{
return;
}
var count = _itemSlotNodes.Count;
if (count == 0)
{
return;
}
var oldSelectIndex = _selectIndex;
var newSelectIndex = _selectIndex + 1;
if (newSelectIndex >= count)
{
newSelectIndex = 0;
}
PrivateSelectItemSlot(oldSelectIndex, newSelectIndex);
}
public void SelectThePreviousItemSlot()
{
if (_itemSlotNodes == null)
{
return;
}
var count = _itemSlotNodes.Count;
if (count == 0)
{
return;
}
var oldSelectIndex = _selectIndex;
var newSelectIndex = _selectIndex - 1;
if (newSelectIndex < 0)
{
newSelectIndex = count - 1;
}
PrivateSelectItemSlot(oldSelectIndex, newSelectIndex);
}
/// <summary>
/// <para>Select an item slot</para>
/// <para>选中某个物品槽</para>
/// </summary>
private void PrivateSelectItemSlot(int oldSelectIndex, int newSelectIndex)
{
if (!SupportSelect || _itemSlotNodes == null || oldSelectIndex == newSelectIndex)
{
return;
}
var oldItemSlotNode = _itemSlotNodes[oldSelectIndex];
oldItemSlotNode.IsSelect = false;
var newItemSlotNode = _itemSlotNodes[newSelectIndex];
newItemSlotNode.IsSelect = true;
HideItem(oldSelectIndex);
DisplayItem(newSelectIndex);
SelectedItemSlotChangeEvent?.Invoke(new SelectedItemSlotChangeEvent
{
NewItemSlotNode = newItemSlotNode,
OldItemSlotNode = oldItemSlotNode
});
_selectIndex = newSelectIndex;
}
/// <summary>
/// <para>HideItem</para>
/// <para>隐藏某个物品</para>
/// </summary>
/// <param name="index"></param>
private void HideItem(int index)
{
var oldItem = _itemSlotNodes?[index].GetItem();
if (oldItem is not Node2D oldNode2D) return;
oldNode2D.ProcessMode = Node.ProcessModeEnum.Disabled;
oldNode2D.Hide();
}
/// <summary>
/// <para>Displays the items in an item slot</para>
/// <para>显示某个物品槽内的物品</para>
/// </summary>
/// <remarks>
///<para>This method can also be used to refresh items held by the character, for example when a new item is dragged to the current display location, then call this method to refresh items held by the character.</para>
///<para>此方法也可用于刷新角色手上持有的物品,例如当新的物品被拖动到当前显示位置,那么请调用此方法刷新角色持有的物品。</para>
/// </remarks>
/// <param name="index"></param>
private void DisplayItem(int index)
{
var item = _itemSlotNodes?[index].GetItem();
if (item is not Node2D newNode2D) return;
newNode2D.ProcessMode = Node.ProcessModeEnum.Inherit;
newNode2D.Show();
}
public void SelectItemSlot(int newSelectIndex)
{
if (newSelectIndex == _selectIndex)
{
return;
}
var safeIndex = GetSafeIndex(newSelectIndex);
if (safeIndex == UnknownIndex)
{
return;
}
PrivateSelectItemSlot(_selectIndex, newSelectIndex);
}
}