Item containers maintain item collections, not item slots.

物品容器将维护物品集合,而不是物品槽。
This commit is contained in:
Cold-Mint 2024-09-22 16:51:42 +08:00
parent 2c6f8804ba
commit 32299877c6
Signed by: Cold-Mint
GPG Key ID: C5A9BF8A98E0CE99
13 changed files with 364 additions and 358 deletions

View File

@ -1,10 +1,11 @@
[gd_scene load_steps=9 format=4 uid="uid://du5ldsp613fei"]
[gd_scene load_steps=10 format=4 uid="uid://du5ldsp613fei"]
[ext_resource type="TileSet" uid="uid://c4wpp12rr44hi" path="res://tileSets/dungeon.tres" id="1_rn2om"]
[ext_resource type="Script" path="res://scripts/map/PlayerSpawn.cs" id="2_6p8mv"]
[ext_resource type="Script" path="res://scripts/map/ItemSpawn.cs" id="3_v1tlc"]
[ext_resource type="Texture2D" uid="uid://drw45jlmfo0su" path="res://sprites/light/White_100.png" id="5_4pssd"]
[ext_resource type="PackedScene" uid="uid://djsh4unystlf0" path="res://prefab/furnitures/MagicSeparator.tscn" id="5_7c8bh"]
[ext_resource type="PackedScene" uid="uid://bq5d2w22wnxrf" path="res://prefab/packsacks/PortableBackpacks.tscn" id="6_0iot7"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_kiih8"]
size = Vector2(507, 251)
@ -80,3 +81,9 @@ texture = ExtResource("5_4pssd")
[node name="RigidBody2D" parent="." instance=ExtResource("5_7c8bh")]
position = Vector2(149, 109)
collision_mask = 0
[node name="RigidBody2D2" parent="." instance=ExtResource("6_0iot7")]
position = Vector2(338, 115)
[node name="RigidBody2D3" parent="." instance=ExtResource("6_0iot7")]
position = Vector2(206, 123)

View File

@ -179,9 +179,7 @@ public sealed partial class AiCharacter : CharacterTemplate
//You must create an item container for the character before you can pick up the item.
//必须为角色创建物品容器后才能拾起物品。
var universalItemContainer = new UniversalItemContainer();
var itemSlotNode = universalItemContainer.AddItemSlot(this);
itemSlotNode?.Hide();
var universalItemContainer = new UniversalItemContainer(1);
ProtectedItemContainer = universalItemContainer;
//Add initial weapon
//添加初始武器

View File

@ -413,14 +413,6 @@ public partial class CharacterTemplate : CharacterBody2D
return false;
}
//Get the currently selected node
//拿到当前选择的节点
var selectItemSlotNode = ItemContainer.GetSelectItemSlotNode();
if (selectItemSlotNode == null)
{
return false;
}
//Check to see if you can fit the item into the container first.
//先检查是否能将物品放入容器。
var canAddItem = ItemContainer.CanAddItem(item);
@ -431,8 +423,8 @@ public partial class CharacterTemplate : CharacterBody2D
//Is it successfully added to the container?
//再检查是否成功的添加到容器内了?
var addSuccess = ItemContainer.AddItem(item);
if (!addSuccess)
var addSuccessNumber = ItemContainer.AddItem(item);
if (addSuccessNumber <= 0)
{
return false;
}
@ -454,7 +446,7 @@ public partial class CharacterTemplate : CharacterBody2D
}
if (_currentItem == null && selectItemSlotNode.GetItem() == item)
if (_currentItem == null && ItemContainer.GetSelectItem() == item)
{
//If the selected item slot in the item container is a newly picked item, and there is no item in the hand, then we put the selected item into the hand.
//如果物品容器内选中的物品槽是刚刚捡到的物品,且手里没有物品持有,那么我们将选中的物品放到手上。
@ -723,7 +715,7 @@ public partial class CharacterTemplate : CharacterBody2D
return;
}
var len = ItemContainer.GetItemSlotCount();
var len = ItemContainer.GetUsedCapacity();
if (len == 0)
{
return;
@ -762,21 +754,19 @@ public partial class CharacterTemplate : CharacterBody2D
/// </param>
protected void ThrowItem(int index, int number, Vector2 velocity)
{
var itemSlotNode = ItemContainer?.GetItemSlotNode(index);
if (itemSlotNode is null) return;
if (number < 0)
if (number == 0)
{
while (!itemSlotNode.IsEmpty())
{
ThrowOneItem(itemSlotNode, velocity);
}
return;
}
else
var item = ItemContainer?.GetItem(index);
if (item is null) return;
//Less than 0, throw everything
//小于0,扔出所有物品
var actualQuantity = number < 0 ? item.Quantity : Math.Min(item.Quantity, number);
for (var i = 0; i < actualQuantity; i++)
{
for (var i = 0; i < number && !itemSlotNode.IsEmpty(); i++)
{
ThrowOneItem(itemSlotNode, velocity);
}
ThrowOneItem(item, velocity);
}
}
@ -784,16 +774,16 @@ public partial class CharacterTemplate : CharacterBody2D
/// <para>Throw item</para>
/// <para>抛出物品</para>
/// </summary>
/// <param name="itemSlotNode"></param>
/// <param name="originalItem"></param>
/// <param name="velocity">
/// <para>The speed to be applied to the item</para>
/// <para>要施加到物品上的速度</para>
/// </param>
private void ThrowOneItem(ItemSlotNode itemSlotNode, Vector2 velocity)
private void ThrowOneItem(IItem originalItem, Vector2 velocity)
{
//Remove the item from the item container
//从物品容器内取出物品
var item = itemSlotNode.CreateItemInstance(1);
var item = originalItem.CreateItem(1);
if (item is not Node2D node2D)
{
return;
@ -847,7 +837,7 @@ public partial class CharacterTemplate : CharacterBody2D
break;
}
itemSlotNode.RemoveItem(1);
originalItem.Quantity -= 1;
}

View File

@ -72,7 +72,7 @@ public partial class Player : CharacterTemplate
//Subscribe to events when the item container is bound to the player.
//在物品容器与玩家绑定时订阅事件。
itemContainer.SelectedItemSlotChangeEvent += SelectedItemSlotChangeEvent;
itemContainer.SelectedItemChangeEvent += SelectedItemChangeEvent;
}
public override void _ExitTree()
@ -82,13 +82,13 @@ public partial class Player : CharacterTemplate
{
//Unsubscribe to events when this object is destroyed.
//此节点被销毁时,取消订阅事件。
ProtectedItemContainer.SelectedItemSlotChangeEvent -= SelectedItemSlotChangeEvent;
ProtectedItemContainer.SelectedItemChangeEvent -= SelectedItemChangeEvent;
}
}
private void SelectedItemSlotChangeEvent(SelectedItemSlotChangeEvent selectedItemSlotChangeEvent)
private void SelectedItemChangeEvent(SelectedItemChangeEvent selectedItemChangeEvent)
{
var item = selectedItemSlotChangeEvent.NewItemSlotNode?.GetItem();
var item = selectedItemChangeEvent.NewItem;
GameSceneDepend.DynamicUiGroup?.HideAllControl();
if (item is Node2D node2D)
{

View File

@ -15,17 +15,9 @@ public partial class HotBar : HBoxContainer
public override void _Ready()
{
base._Ready();
_itemContainer = new UniversalItemContainer();
_itemContainer = new UniversalItemContainer(Config.HotBarSize);
_itemContainer.SupportSelect = true;
NodeUtils.DeleteAllChild(this);
for (var i = 0; i < Config.HotBarSize; i++)
{
var itemSlotNode = _itemContainer.AddItemSlot(this);
if (itemSlotNode != null)
{
itemSlotNode.BackpackAllowed = true;
}
}
}
@ -36,14 +28,14 @@ public partial class HotBar : HBoxContainer
{
//Mouse wheel down
//鼠标滚轮向下
_itemContainer?.SelectTheNextItemSlot();
_itemContainer?.SelectNextItem();
}
if (Input.IsActionJustPressed("hotbar_previous"))
{
//Mouse wheel up
//鼠标滚轮向上
_itemContainer?.SelectThePreviousItemSlot();
_itemContainer?.SelectPreviousItem();
}
if (Input.IsActionJustPressed("hotbar_1"))
@ -106,7 +98,7 @@ public partial class HotBar : HBoxContainer
return;
}
_itemContainer.SelectItemSlot(shortcutKeyIndex);
_itemContainer.SelectItem(shortcutKeyIndex);
}
public IItemContainer? GetItemContainer()

View File

@ -33,13 +33,48 @@ public interface IItem
/// <para>当前的数量</para>
/// </summary>
int Quantity { get; set; }
/// <summary>
/// <para>MaxItemQuantity</para>
/// <para>最大物品数量</para>
/// </summary>
int MaxQuantity { get; }
/// <summary>
/// <para>Check or not</para>
/// <para>是否选中</para>
/// </summary>
bool IsSelect { get; set; }
/// <summary>
/// <para>Calculate how many items can be merged with other items</para>
/// <para>计算当前物品可与其他物品合并多少个</para>
/// </summary>
/// <param name="other"></param>
/// <param name="unallocatedQuantity">
///<para>The amount yet to be allocated(This method doesn't actually change the number of iitems, so you need to allocate an int variable to keep track of how many items remain unallocated.)</para>
///<para>尚未分配的数量在此方法并不会实际改变IItem的数量故您需要分配一个int型变量记录还有多少个物品尚未分配</para>
/// </param>
/// <returns>
///<para>Number of mergable numbers. 0 indicates that the number cannot be merged. Greater than 0 indicates that the number can be merged.</para>
///<para>可合并的数量0为不能合并大于0可合并。</para>
/// </returns>
int MergeableItemCount(IItem other, int unallocatedQuantity);
/// <summary>
/// <para>Create a new item instance</para>
/// <para>创建新的物品实例</para>
/// </summary>
/// <param name="number">
///<para>Quantity (pass in a value less than 0 to create an instance using all the quantities of built-in items)</para>
///<para>数量传入小于0的值使用内置物品的所有数量创建实例</para>
/// </param>
/// <returns>
///<para>Newly created item</para>
///<para>新创建的物品</para>
/// </returns>
IItem? CreateItem(int number);
/// <summary>
/// <para>Execute when current item is used <br/> e.g. when player clicks left mouse button with current item in hand</para>
/// <para>当前项被使用时执行 <br/> e.g. 当玩家用鼠标左键点击当前物品时</para>

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using ColdMint.scripts.map.events;
using Godot;
namespace ColdMint.scripts.inventory;
@ -13,13 +12,13 @@ namespace ColdMint.scripts.inventory;
///<para>Item containers can store items. Things like backpacks and Hotbars are containers with visual pages.</para>
///<para>物品容器可以储存物品。像背包和hotbar是具有可视化页面的容器。</para>
/// </remarks>
public interface IItemContainer : IEnumerable<ItemSlotNode>
public interface IItemContainer : IEnumerable<IItem>
{
/// <summary>
/// <para>This event is triggered when the selected item slot changes</para>
/// <para>当选中的物品改变时,触发此事件</para>
/// <para>This event is triggered when the selected item changes</para>
/// <para>当选中的物品改变时,触发此事件</para>
/// </summary>
Action<SelectedItemSlotChangeEvent>? SelectedItemSlotChangeEvent { get; set; }
Action<SelectedItemChangeEvent>? SelectedItemChangeEvent { get; set; }
/// <summary>
/// <para>Can the specified item be added to the container?</para>
@ -34,15 +33,18 @@ public interface IItemContainer : IEnumerable<ItemSlotNode>
/// <para>实现添加物品的方法</para>
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
bool AddItem(IItem item);
/// <returns>
///<para>How many items were successfully added. The addition failed, and 0 was returned.</para>
///<para>有多少个物品被成功添加了。添加失败返回0</para>
/// </returns>
int AddItem(IItem item);
/// <summary>
/// <para>Whether this item container supports checking</para>
/// <para>此物品容器是否支持选中</para>
/// </summary>
public bool SupportSelect { get; set; }
/// <summary>
/// <para>Gets the selected location</para>
@ -52,104 +54,88 @@ public interface IItemContainer : IEnumerable<ItemSlotNode>
int GetSelectIndex();
/// <summary>
/// <para>Gets the currently selected node</para>
/// <para>获取当前选中的节点</para>
/// <para>Gets the currently selected item</para>
/// <para>获取当前选中的物品</para>
/// </summary>
/// <returns></returns>
ItemSlotNode? GetSelectItemSlotNode();
IItem? GetSelectItem();
/// <summary>
/// <para>Removes an item from the inventory at the currently selected location</para>
/// <para>移除当前选中位置物品栏内的物品</para>
/// <para>Gets the item in the specified location</para>
/// <para>获取指定位置的物品</para>
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
IItem? GetItem(int index);
/// <summary>
/// <para>Gets the item in the specified location, equivalent to <see cref="GetItem"/></para>
/// <para>获取指定位置的物品,等同于<see cref="GetItem"/></para>
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
IItem? this[int index] => GetItem(index);
/// <summary>
/// <para>Removes the item from the currently selected location</para>
/// <para>移除当前选中位置的物品</para>
/// </summary>
/// <param name="number">
/// <para>Quantity to be removed, inputs below zero represent all items</para>
/// <para>要删除的数量小于0的输入代表全部物品</para>
/// </param>
/// <returns>
/// <para>The remaining number, if the number of items in the current item stack is less than the specified number. Otherwise,0</para>
/// <para>若物品槽内物品少于指定的数量返回相差的数量。否则返回0</para>
///<para>How many items were actually removed</para>
///<para>实际移除了多少个物品</para>
/// </returns>
int RemoveItemFromItemSlotBySelectIndex(int number);
int RemoveSelectItem(int number);
/// <summary>
/// <para>Gets the number of item slots</para>
/// <para>获取物品槽的数量</para>
/// <para>Deduct the number of items in the specified location</para>
/// <para>扣除指定位置的物品数量</para>
/// </summary>
/// <returns></returns>
int GetItemSlotCount();
/// <summary>
/// <para>Gets the item slot for the specified location</para>
/// <para>获取指定位置的物品槽</para>
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
ItemSlotNode? GetItemSlotNode(int index);
/// <summary>
/// <para>Gets the item slot for the specified location, equals to <see cref="GetItemSlotNode"/></para>
/// <para>获取指定位置的物品槽,等同于<see cref="GetItemSlotNode"/></para>
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
ItemSlotNode? this[int index] => GetItemSlotNode(index);
/// <summary>
/// <para>Removes an item from the item slot in the specified location</para>
/// <para>在指定位置的物品槽内移除物品</para>
/// </summary>
/// <param name="itemSlotIndex"></param>
/// <param name="itemIndex"></param>
/// <param name="number">
/// <para>Quantity to be removed, inputs below zero represent all items</para>
/// <para>要删除的数量小于0的输入代表全部物品</para>
/// </param>
/// <returns>
/// <para>The remaining number, if the number of items in the current item stack is less than the specified number. Otherwise,0</para>
/// <para>若物品槽内物品少于指定的数量返回相差的数量。否则返回0</para>
///<para>How many items were actually removed</para>
///<para>实际移除了多少个物品</para>
/// </returns>
int RemoveItemFromItemSlot(int itemSlotIndex, int number);
int RemoveItem(int itemIndex, int number);
/// <summary>
/// <para>Based on the given item, match the item slots where it can be added to </para>
/// <para>根据给定的物品,匹配可放置它的物品槽</para>
/// <para>Gets the used capacity of the item container</para>
/// <para>获取物品容器已使用的容量</para>
/// </summary>
/// <param name="item"></param>
/// <returns>
/// <para>Return null if there is no slot to place the item in</para>
/// <para>若没有槽可放置此物品则返回null</para>
/// </returns>
ItemSlotNode? Match(IItem item);
/// <returns></returns>
int GetUsedCapacity();
/// <summary>
/// <para>AddItemSlot</para>
/// <para>添加物品槽</para>
/// <para>Gets the total capacity of the item container</para>
/// <para>获取物品容器的总容量</para>
/// </summary>
/// <param name="rootNode"></param>
ItemSlotNode? AddItemSlot(Node rootNode);
/// <returns></returns>
int GetTotalCapacity();
/// <summary>
/// <para>SelectTheNextItemSlot</para>
/// <para>选择下一个物品</para>
/// <para>Select the next item</para>
/// <para>选择下一个物品</para>
/// </summary>
void SelectTheNextItemSlot();
void SelectNextItem();
/// <summary>
/// <para>SelectThePreviousItemSlot</para>
/// <para>选择上一个物品</para>
/// <para>Select the previous item</para>
/// <para>选择上一个物品</para>
/// </summary>
void SelectThePreviousItemSlot();
void SelectPreviousItem();
/// <summary>
/// <para>选择物品</para>
/// <para>SelectItemSlot</para>
/// <para>选择物品</para>
/// <para>Select item</para>
/// </summary>
/// <param name="newSelectIndex"></param>
void SelectItemSlot(int newSelectIndex);
/// <param name="index"></param>
void SelectItem(int index);
}

View File

@ -141,58 +141,7 @@ public partial class ItemSlotNode : MarginContainer
return _item;
}
/// <summary>
/// <para>CreateItemInstance</para>
/// <para>创建物品槽内的物品实例</para>
/// </summary>
/// <param name="number">
///<para>Quantity (pass in a value less than 0 to create an instance using all the quantities of built-in items)</para>
///<para>数量传入小于0的值使用内置物品的所有数量创建实例</para>
/// </param>
/// <returns>
///<para>Newly created item</para>
///<para>新创建的物品</para>
/// </returns>
public IItem? CreateItemInstance(int number)
{
if (number == 0)
{
return null;
}
if (_item is not Node2D node2D)
{
return null;
}
var duplicate = node2D.Duplicate();
if (node2D is PickAbleTemplate pickAbleTemplate)
{
pickAbleTemplate.CopyAttributes(duplicate);
}
if (duplicate is not Node2D newNode2D)
{
return null;
}
newNode2D.GlobalPosition = node2D.GlobalPosition;
if (duplicate is not IItem newItem)
{
return null;
}
if (number < 0)
{
newItem.Quantity = _item.Quantity;
}
else
{
newItem.Quantity = number >= _item.Quantity ? _item.Quantity : number;
}
return newItem;
}
/// <summary>
/// <para>Clean out the items in the item slot</para>

View File

@ -45,22 +45,8 @@ public partial class Packsack : PickAbleTemplate
public override void _Ready()
{
base._Ready();
ItemContainer = new UniversalItemContainer();
ItemContainer = new UniversalItemContainer(NumberSlots);
ItemContainer.SupportSelect = false;
//When the backpack is created, the item slot is generated.
//当背包被创建时,物品槽就被生成出来了。
for (var i = 0; i < NumberSlots; i++)
{
var itemSlotNode = ItemContainer.AddItemSlot(this);
if (itemSlotNode == null)
{
continue;
}
itemSlotNode.BackpackAllowed = BackpackAllowed;
itemSlotNode.Hide();
}
GameSceneDepend.DynamicUiGroup?.RegisterControl(Path, () =>
{
var packedScene = GD.Load<PackedScene>(Path);

View File

@ -1,10 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ColdMint.scripts.map.events;
using ColdMint.scripts.utils;
using Godot;
using JetBrains.Annotations;
namespace ColdMint.scripts.inventory;
@ -13,11 +10,9 @@ namespace ColdMint.scripts.inventory;
/// <para>UniversalItemContainer</para>
/// <para>通用的物品容器</para>
/// </summary>
public class UniversalItemContainer : IItemContainer
public class UniversalItemContainer(int totalCapacity) : IItemContainer
{
private readonly List<ItemSlotNode>? _itemSlotNodes = [];
private readonly PackedScene? _itemSlotPackedScene = GD.Load<PackedScene>("res://prefab/ui/ItemSlot.tscn");
private readonly List<IItem> _itemList = [];
/// <summary>
/// <para>UnknownIndex</para>
@ -25,13 +20,14 @@ public class UniversalItemContainer : IItemContainer
/// </summary>
private const int UnknownIndex = -1;
//_selectIndex defaults to 0.
//_selectIndex默认为0.
private int _selectIndex;
[MustDisposeResource]
public IEnumerator<ItemSlotNode> GetEnumerator()
public IEnumerator<IItem> GetEnumerator()
{
return _itemSlotNodes?.GetEnumerator() ?? Enumerable.Empty<ItemSlotNode>().GetEnumerator();
return _itemList.GetEnumerator();
}
[MustDisposeResource]
@ -40,22 +36,101 @@ public class UniversalItemContainer : IItemContainer
return GetEnumerator();
}
public Action<SelectedItemSlotChangeEvent>? SelectedItemSlotChangeEvent { get; set; }
public Action<SelectedItemChangeEvent>? SelectedItemChangeEvent { get; set; }
public bool CanAddItem(IItem item)
{
return Match(item) != null;
}
public bool AddItem(IItem item)
{
var itemSlotNode = Match(item);
if (itemSlotNode == null)
//If the capacity is not full, directly return to add items
//如果未占满容量,直接返回可添加物品
if (GetUsedCapacity() < GetTotalCapacity())
{
return true;
}
if (item.MaxQuantity == 1)
{
//New items do not support overlay, capacity is full, return cannot add.
//新物品不支持叠加,容量已满,返回不能添加。
return false;
}
return itemSlotNode.AddItem(item);
//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;
}
public int AddItem(IItem item)
{
if (item.MaxQuantity == 1)
{
if (GetUsedCapacity() >= GetTotalCapacity())
{
//Items cannot be stacked and cannot be added if the capacity is full.
//物品不能叠加,且容量已满,则无法添加。
return 0;
}
_itemList.Add(item);
return item.Quantity;
}
//There can be more than one item, try to share equally.
//物品可有多个,尝试均摊。
var originalQuantity = item.Quantity;
foreach (var unitItem in _itemList)
{
var number = unitItem.MergeableItemCount(item, item.Quantity);
if (number == 0)
{
continue;
}
item.Quantity -= number;
unitItem.Quantity += number;
if (item.Quantity < 1)
{
//New items are fully shared.
//新物品完全被均摊。
return originalQuantity;
}
}
if (item.Quantity < 1)
{
//After traversing to the last item, the new item is fully shared.
//在遍历到最后的物品,新物品完全被均摊。
return originalQuantity;
}
//New items have some left over.
//新物品有一些剩余。
if (GetUsedCapacity() >= GetTotalCapacity())
{
//The capacity is full. The remaining capacity cannot be stored.
//容量已满,无法存放剩余。
return originalQuantity - item.Quantity;
}
//Add the rest to the container.
//添加剩余到容器内。
_itemList.Add(item);
return originalQuantity;
}
public bool SupportSelect { get; set; }
@ -65,76 +140,34 @@ public class UniversalItemContainer : IItemContainer
return _selectIndex;
}
public ItemSlotNode? GetSelectItemSlotNode()
public IItem? GetSelectItem()
{
if (_itemSlotNodes == null || _itemSlotNodes.Count == 0)
{
return null;
}
if (_selectIndex < _itemSlotNodes.Count)
{
//Prevent subscripts from going out of bounds.
//防止下标越界。
return _itemSlotNodes[_selectIndex];
}
return null;
return _itemList.Count == 0 ? null : _itemList[_selectIndex];
}
public int RemoveItemFromItemSlotBySelectIndex(int number) => RemoveItemFromItemSlot(_selectIndex, number);
public int GetItemSlotCount()
public IItem? GetItem(int index)
{
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;
return null;
}
var itemSlot = _itemSlotNodes[safeIndex];
return itemSlot.RemoveItem(number);
return _itemList[safeIndex];
}
/// <summary>
/// <para>Gets a secure subscript index</para>
/// <para>获取安全的下标索引</para>
/// </summary>
/// <param name="itemSlotIndex"></param>
/// <param name="index"></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)
private int GetSafeIndex(int index)
{
if (_itemSlotNodes == null)
{
return UnknownIndex;
}
var count = _itemSlotNodes.Count;
var count = _itemList.Count;
if (count == 0)
{
//Prevents the dividend from being 0
@ -142,50 +175,63 @@ public class UniversalItemContainer : IItemContainer
return UnknownIndex;
}
return itemSlotIndex % count;
return index % count;
}
public ItemSlotNode? Match(IItem item)
public int RemoveSelectItem(int number)
{
//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));
return RemoveItem(_selectIndex, number);
}
public ItemSlotNode? AddItemSlot(Node rootNode)
public int RemoveItem(int itemIndex, int number)
{
if (_itemSlotNodes == null || _itemSlotPackedScene == null)
if (number == 0)
{
return null;
return 0;
}
var itemSlotNode = NodeUtils.InstantiatePackedScene<ItemSlotNode>(_itemSlotPackedScene);
if (itemSlotNode == null)
var safeIndex = GetSafeIndex(itemIndex);
if (safeIndex == UnknownIndex)
{
return null;
}
NodeUtils.CallDeferredAddChild(rootNode, itemSlotNode);
if (SupportSelect)
{
itemSlotNode.IsSelect = _itemSlotNodes.Count == _selectIndex;
}
else
{
itemSlotNode.IsSelect = false;
return 0;
}
_itemSlotNodes.Add(itemSlotNode);
return itemSlotNode;
var item = _itemList[safeIndex];
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(safeIndex);
return originalQuantity;
}
var removed = Math.Min(number, item.Quantity);
item.Quantity -= removed;
if (item.Quantity < 1)
{
_itemList.RemoveAt(safeIndex);
}
return removed;
}
public void SelectTheNextItemSlot()
public int GetUsedCapacity()
{
if (_itemSlotNodes == null)
{
return;
}
return _itemList.Count;
}
var count = _itemSlotNodes.Count;
public int GetTotalCapacity()
{
return totalCapacity;
}
public void SelectNextItem()
{
var count = _itemList.Count;
if (count == 0)
{
return;
@ -195,20 +241,15 @@ public class UniversalItemContainer : IItemContainer
var newSelectIndex = _selectIndex + 1;
if (newSelectIndex >= count)
{
newSelectIndex = 0;
newSelectIndex = count - 1;
}
PrivateSelectItemSlot(oldSelectIndex, newSelectIndex);
PrivateSelectItem(oldSelectIndex, newSelectIndex);
}
public void SelectThePreviousItemSlot()
public void SelectPreviousItem()
{
if (_itemSlotNodes == null)
{
return;
}
var count = _itemSlotNodes.Count;
var count = _itemList.Count;
if (count == 0)
{
return;
@ -221,77 +262,43 @@ public class UniversalItemContainer : IItemContainer
newSelectIndex = count - 1;
}
PrivateSelectItemSlot(oldSelectIndex, newSelectIndex);
PrivateSelectItem(oldSelectIndex, newSelectIndex);
}
/// <summary>
/// <para>Select an item slot</para>
/// <para>选中某个物品槽</para>
/// <para>Private methods for selecting items</para>
/// <para>选择物品的私有方法</para>
/// </summary>
private void PrivateSelectItemSlot(int oldSelectIndex, int newSelectIndex)
/// <param name="oldIndex"></param>
/// <param name="newIndex"></param>
private void PrivateSelectItem(int oldIndex, int newIndex)
{
if (!SupportSelect || _itemSlotNodes == null || oldSelectIndex == newSelectIndex)
if (!SupportSelect || oldIndex == newIndex)
{
return;
}
var oldItemSlotNode = _itemSlotNodes[oldSelectIndex];
oldItemSlotNode.IsSelect = false;
var newItemSlotNode = _itemSlotNodes[newSelectIndex];
newItemSlotNode.IsSelect = true;
HideItem(oldSelectIndex);
DisplayItem(newSelectIndex);
SelectedItemSlotChangeEvent?.Invoke(new SelectedItemSlotChangeEvent
var oldItem = _itemList[oldIndex];
oldItem.IsSelect = false;
var newItem= _itemList[newIndex];
newItem.IsSelect = true;
SelectedItemChangeEvent?.Invoke(new SelectedItemChangeEvent
{
NewItemSlotNode = newItemSlotNode,
OldItemSlotNode = oldItemSlotNode
NewItem = newItem,
OldItem = oldItem
});
_selectIndex = newSelectIndex;
_selectIndex = newIndex;
}
/// <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)
public void SelectItem(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);
var safeIndex = GetSafeIndex(index);
if (safeIndex == UnknownIndex)
{
return;
}
PrivateSelectItemSlot(_selectIndex, newSelectIndex);
PrivateSelectItem(_selectIndex, safeIndex);
}
}

View File

@ -63,11 +63,12 @@ public partial class PacksackUi : UiLoaderTemplate
}
NodeUtils.DeleteAllChild(_hFlowContainer);
foreach (var itemSlotNode in itemContainer)
{
itemSlotNode.Reparent(_hFlowContainer);
itemSlotNode.Show();
}
//todo:实现使用物品数据刷新物品槽的方法。
// foreach (var item in itemContainer)
// {
// itemSlotNode.Reparent(_hFlowContainer);
// itemSlotNode.Show();
// }
}
/// <summary>

View File

@ -6,19 +6,19 @@ namespace ColdMint.scripts.map.events;
/// <para>Selected item slot changes event</para>
/// <para>选中的物品槽改变事件</para>
/// </summary>
public class SelectedItemSlotChangeEvent
public class SelectedItemChangeEvent
{
/// <summary>
/// <para></para>
/// <para>新选中的物品</para>
/// <para>Newly selected item</para>
/// <para>新选中的物品</para>
/// </summary>
public ItemSlotNode? NewItemSlotNode { get; set; }
public IItem? NewItem { get; set; }
/// <summary>
/// <para>Lost the selected item slot</para>
/// <para>失去选中的物品</para>
/// <para>Lost the selected item</para>
/// <para>失去选中的物品</para>
/// </summary>
// ReSharper disable UnusedAutoPropertyAccessor.Global
public ItemSlotNode? OldItemSlotNode { get; set; }
public IItem? OldItem { get; set; }
// ReSharper restore UnusedAutoPropertyAccessor.Global
}

View File

@ -77,8 +77,63 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
public bool Picked { get; set; }
public int MaxQuantity { get; set; }
public bool IsSelect { get; set; }
private Label? _tipLabel;
public IItem? CreateItem(int number)
{
if (number == 0)
{
return null;
}
var duplicate = Duplicate();
if (duplicate is PickAbleTemplate pickAbleTemplate)
{
pickAbleTemplate.CopyAttributes(this);
}
if (duplicate is not Node2D newNode2D)
{
return null;
}
newNode2D.GlobalPosition = GlobalPosition;
if (duplicate is not IItem newItem)
{
duplicate.QueueFree();
return null;
}
if (number < 0)
{
newItem.Quantity = Quantity;
}
else
{
newItem.Quantity = Math.Min(Quantity, number);
}
return newItem;
}
public int MergeableItemCount(IItem other, int unallocatedQuantity)
{
var freeQuantity = MaxQuantity - Quantity;
if (freeQuantity == 0)
{
return 0;
}
if (other.Id != Id)
{
return 0;
}
return Math.Min(freeQuantity, unallocatedQuantity);
}
public virtual void Use(Node2D? owner, Vector2 targetGlobalPosition)
{