2024-06-12 17:18:55 +00:00
|
|
|
|
using System;
|
2024-06-13 02:43:54 +00:00
|
|
|
|
using System.Collections;
|
2024-06-12 17:18:55 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
2024-06-12 13:33:29 +00:00
|
|
|
|
using ColdMint.scripts.character;
|
2024-06-12 17:18:55 +00:00
|
|
|
|
using ColdMint.scripts.item;
|
2024-06-13 05:53:10 +00:00
|
|
|
|
using ColdMint.scripts.item.itemStacks;
|
2024-06-12 13:33:29 +00:00
|
|
|
|
using ColdMint.scripts.utils;
|
|
|
|
|
using Godot;
|
2024-06-13 02:43:54 +00:00
|
|
|
|
using JetBrains.Annotations;
|
|
|
|
|
|
2024-06-12 13:33:29 +00:00
|
|
|
|
namespace ColdMint.scripts.inventory;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// <para>UniversalItemContainer</para>
|
|
|
|
|
/// <para>通用的物品容器</para>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class UniversalItemContainer : IItemContainer
|
|
|
|
|
{
|
|
|
|
|
private readonly PackedScene? _itemSlotPackedScene = GD.Load<PackedScene>("res://prefab/ui/ItemSlot.tscn");
|
|
|
|
|
|
2024-06-14 04:44:31 +00:00
|
|
|
|
private readonly List<ItemSlotNode>? _itemSlotNodes = [];
|
2024-06-12 13:33:29 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// <para>Character</para>
|
|
|
|
|
/// <para>角色</para>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public CharacterTemplate? CharacterTemplate { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// <para>UnknownIndex</para>
|
|
|
|
|
/// <para>未知位置</para>
|
|
|
|
|
/// </summary>
|
|
|
|
|
private const int UnknownIndex = -1;
|
|
|
|
|
|
|
|
|
|
//_selectIndex默认为0.
|
|
|
|
|
private int _selectIndex;
|
|
|
|
|
|
2024-06-12 19:07:55 +00:00
|
|
|
|
public bool CanAddItem(IItem item)
|
2024-06-12 13:33:29 +00:00
|
|
|
|
{
|
2024-06-12 17:18:55 +00:00
|
|
|
|
return Match(item) != null;
|
2024-06-12 13:33:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-12 19:07:55 +00:00
|
|
|
|
public bool AddItem(IItem item)
|
2024-06-12 13:33:29 +00:00
|
|
|
|
{
|
2024-06-12 17:18:55 +00:00
|
|
|
|
var itemSlotNode = Match(item);
|
2024-06-12 13:33:29 +00:00
|
|
|
|
if (itemSlotNode == null)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-12 17:18:55 +00:00
|
|
|
|
return itemSlotNode.AddItem(item);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-14 04:22:44 +00:00
|
|
|
|
public int CanAddItemStack(IItemStack itemStack)
|
|
|
|
|
{
|
|
|
|
|
var testItem = itemStack.GetItem();
|
|
|
|
|
if (testItem is null) return 0;
|
|
|
|
|
var slots = MatchAll(slot => slot.CanAddItem(testItem));
|
|
|
|
|
return
|
|
|
|
|
Math.Min(itemStack.Quantity,
|
2024-06-17 14:12:51 +00:00
|
|
|
|
slots.Select(slot => slot.CanAddItemStack(itemStack)).Sum());
|
2024-06-14 04:22:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-12 17:18:55 +00:00
|
|
|
|
public bool AddItemStack(IItemStack itemStack)
|
|
|
|
|
{
|
2024-06-16 09:22:43 +00:00
|
|
|
|
ItemSlotNode? itemSlotNode = Match(itemStack);
|
|
|
|
|
while (itemSlotNode is not null)
|
2024-06-12 17:18:55 +00:00
|
|
|
|
{
|
|
|
|
|
if (itemSlotNode.AddItemStack(itemStack))
|
|
|
|
|
return true;
|
2024-06-17 14:12:51 +00:00
|
|
|
|
|
2024-06-16 09:22:43 +00:00
|
|
|
|
itemSlotNode = Match(itemStack);
|
2024-06-12 17:18:55 +00:00
|
|
|
|
}
|
2024-06-16 09:22:43 +00:00
|
|
|
|
|
|
|
|
|
return false;
|
2024-06-12 13:33:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-12 19:07:55 +00:00
|
|
|
|
public IItem? PickItemFromItemSlotBySelectIndex() => PickItemFromItemSlot(_selectIndex);
|
2024-06-12 17:18:55 +00:00
|
|
|
|
|
|
|
|
|
public IItemStack? PickItemsFromItemSlotBySelectIndex(int value) => PickItemsFromItemSlot(_selectIndex, value);
|
|
|
|
|
|
|
|
|
|
public int RemoveItemFromItemSlotBySelectIndex(int number) => RemoveItemFromItemSlot(_selectIndex, number);
|
2024-06-12 13:33:29 +00:00
|
|
|
|
|
|
|
|
|
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];
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-12 19:07:55 +00:00
|
|
|
|
public IItem? PickItemFromItemSlot(int itemSlotIndex)
|
2024-06-12 13:33:29 +00:00
|
|
|
|
{
|
2024-06-12 17:18:55 +00:00
|
|
|
|
if (_itemSlotNodes == null) return null;
|
2024-06-12 13:33:29 +00:00
|
|
|
|
var safeIndex = GetSafeIndex(itemSlotIndex);
|
|
|
|
|
if (safeIndex == UnknownIndex)
|
|
|
|
|
{
|
2024-06-12 17:18:55 +00:00
|
|
|
|
return null;
|
2024-06-12 13:33:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var itemSlot = _itemSlotNodes[safeIndex];
|
2024-06-12 17:18:55 +00:00
|
|
|
|
return itemSlot.PickItem();
|
2024-06-12 13:33:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-12 17:18:55 +00:00
|
|
|
|
public IItemStack? PickItemsFromItemSlot(int itemSlotIndex, int value)
|
2024-06-12 13:33:29 +00:00
|
|
|
|
{
|
2024-06-12 17:18:55 +00:00
|
|
|
|
if (_itemSlotNodes == null) return null;
|
|
|
|
|
var safeIndex = GetSafeIndex(itemSlotIndex);
|
|
|
|
|
if (safeIndex == UnknownIndex)
|
2024-06-12 13:33:29 +00:00
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-12 17:18:55 +00:00
|
|
|
|
var itemSlot = _itemSlotNodes[safeIndex];
|
|
|
|
|
return itemSlot.PickItems(value);
|
|
|
|
|
}
|
2024-06-12 13:33:29 +00:00
|
|
|
|
|
2024-06-12 17:18:55 +00:00
|
|
|
|
public int RemoveItemFromItemSlot(int itemSlotIndex, int number)
|
|
|
|
|
{
|
|
|
|
|
if (_itemSlotNodes == null) return number;
|
|
|
|
|
var safeIndex = GetSafeIndex(itemSlotIndex);
|
|
|
|
|
if (safeIndex == UnknownIndex)
|
2024-06-12 13:33:29 +00:00
|
|
|
|
{
|
2024-06-12 17:18:55 +00:00
|
|
|
|
return number;
|
2024-06-12 13:33:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-12 17:18:55 +00:00
|
|
|
|
var itemSlot = _itemSlotNodes[safeIndex];
|
|
|
|
|
return itemSlot.RemoveItem(number);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-12 19:07:55 +00:00
|
|
|
|
public ItemSlotNode? Match(IItem item)
|
2024-06-12 17:18:55 +00:00
|
|
|
|
{
|
|
|
|
|
//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? Match(IItemStack stack)
|
|
|
|
|
{
|
2024-06-16 12:18:44 +00:00
|
|
|
|
var item = stack.GetItem();
|
|
|
|
|
return item == null ? null : _itemSlotNodes?.FirstOrDefault(itemSlotNode => itemSlotNode.CanAddItem(item));
|
2024-06-12 17:18:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-14 04:22:44 +00:00
|
|
|
|
public ItemSlotNode? Match(Func<ItemSlotNode, bool> predicate)
|
2024-06-12 17:18:55 +00:00
|
|
|
|
{
|
2024-06-14 04:22:44 +00:00
|
|
|
|
return _itemSlotNodes?.FirstOrDefault(predicate);
|
2024-06-12 17:18:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-14 04:22:44 +00:00
|
|
|
|
public IEnumerable<ItemSlotNode> MatchAll(Func<ItemSlotNode, bool> predicate)
|
2024-06-12 17:18:55 +00:00
|
|
|
|
{
|
2024-06-14 04:22:44 +00:00
|
|
|
|
return from node in _itemSlotNodes where predicate(node) select node;
|
2024-06-12 13:33:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// <para>Gets a secure subscript index</para>
|
|
|
|
|
/// <para>获取安全的下标索引</para>
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="itemSlotIndex"></param>
|
|
|
|
|
/// <returns>
|
2024-06-12 17:18:55 +00:00
|
|
|
|
/// <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>
|
2024-06-12 13:33:29 +00:00
|
|
|
|
/// </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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// <para>Add items tank</para>
|
|
|
|
|
/// <para>添加物品槽</para>
|
|
|
|
|
/// </summary>
|
2024-06-17 14:12:51 +00:00
|
|
|
|
public ItemSlotNode? AddItemSlot(Node rootNode)
|
2024-06-12 13:33:29 +00:00
|
|
|
|
{
|
|
|
|
|
if (_itemSlotNodes == null || _itemSlotPackedScene == null)
|
|
|
|
|
{
|
2024-06-17 14:12:51 +00:00
|
|
|
|
return null;
|
2024-06-12 13:33:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var itemSlotNode = NodeUtils.InstantiatePackedScene<ItemSlotNode>(_itemSlotPackedScene, rootNode);
|
|
|
|
|
if (itemSlotNode == null)
|
|
|
|
|
{
|
2024-06-17 14:12:51 +00:00
|
|
|
|
return null;
|
2024-06-12 13:33:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-16 09:22:43 +00:00
|
|
|
|
itemSlotNode.IsSelect = (_itemSlotNodes.Count) == _selectIndex;
|
2024-06-12 13:33:29 +00:00
|
|
|
|
_itemSlotNodes.Add(itemSlotNode);
|
2024-06-17 14:12:51 +00:00
|
|
|
|
return itemSlotNode;
|
2024-06-12 13:33:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SelectItemSlot(int newSelectIndex)
|
|
|
|
|
{
|
|
|
|
|
if (newSelectIndex == _selectIndex)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var safeIndex = GetSafeIndex(newSelectIndex);
|
|
|
|
|
if (safeIndex == UnknownIndex)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PrivateSelectItemSlot(_selectIndex, newSelectIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// <para>Select an item slot</para>
|
|
|
|
|
/// <para>选中某个物品槽</para>
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void PrivateSelectItemSlot(int oldSelectIndex, int newSelectIndex)
|
|
|
|
|
{
|
|
|
|
|
if (oldSelectIndex == newSelectIndex)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_itemSlotNodes == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_itemSlotNodes[oldSelectIndex].IsSelect = false;
|
|
|
|
|
_itemSlotNodes[newSelectIndex].IsSelect = true;
|
|
|
|
|
var oldItem = _itemSlotNodes[oldSelectIndex].GetItem();
|
|
|
|
|
if (oldItem is Node2D oldNode2D)
|
|
|
|
|
{
|
|
|
|
|
oldNode2D.ProcessMode = Node.ProcessModeEnum.Disabled;
|
|
|
|
|
oldNode2D.Hide();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var item = _itemSlotNodes[newSelectIndex].GetItem();
|
|
|
|
|
switch (item)
|
|
|
|
|
{
|
|
|
|
|
case null:
|
|
|
|
|
{
|
|
|
|
|
if (CharacterTemplate != null)
|
|
|
|
|
{
|
|
|
|
|
CharacterTemplate.CurrentItem = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case Node2D node2D:
|
|
|
|
|
{
|
|
|
|
|
node2D.ProcessMode = Node.ProcessModeEnum.Inherit;
|
|
|
|
|
node2D.Show();
|
|
|
|
|
if (CharacterTemplate != null)
|
|
|
|
|
{
|
|
|
|
|
CharacterTemplate.CurrentItem = node2D;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
if (CharacterTemplate != null)
|
|
|
|
|
{
|
|
|
|
|
CharacterTemplate.CurrentItem = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_selectIndex = newSelectIndex;
|
|
|
|
|
}
|
2024-06-13 02:43:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[MustDisposeResource]
|
|
|
|
|
public IEnumerator<ItemSlotNode> GetEnumerator()
|
|
|
|
|
{
|
|
|
|
|
return _itemSlotNodes?.GetEnumerator() ?? Enumerable.Empty<ItemSlotNode>().GetEnumerator();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[MustDisposeResource]
|
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
|
|
|
{
|
|
|
|
|
return GetEnumerator();
|
|
|
|
|
}
|
2024-06-12 13:33:29 +00:00
|
|
|
|
}
|