Remove item slots.

移除物品槽位。
This commit is contained in:
Cold-Mint 2024-06-21 19:16:40 +08:00
parent 5d6d42aa54
commit 309a7b73d2
Signed by: Cold-Mint
GPG Key ID: C5A9BF8A98E0CE99
16 changed files with 307 additions and 1268 deletions

View File

@ -332,9 +332,8 @@ public partial class CharacterTemplate : CharacterBody2D
pickAbleTemplate.Sleeping = true;
}
var itemStack = selectItemSlotNode.GetItemStack();
var itemFromStack = itemStack?.GetItem();
if (itemFromStack != null && itemFromStack == item && _currentItem == null)
var selectItem = selectItemSlotNode.GetItem();
if (_currentItem == null)
{
//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.
//如果物品容器内选中的物品槽是刚刚捡到的物品,且手里没有物品持有,那么我们将选中的物品放到手上。
@ -667,7 +666,7 @@ public partial class CharacterTemplate : CharacterBody2D
{
//Pick an item from the item container
//从物品容器内取出一个物品
var item = itemSlotNode.PickItem();
var item = itemSlotNode.GetItem();
if (item is not Node2D node2D)
{

View File

@ -87,7 +87,7 @@ public partial class Player : CharacterTemplate
private void SelectedItemSlotChangeEvent(SelectedItemSlotChangeEvent selectedItemSlotChangeEvent)
{
var item = selectedItemSlotChangeEvent.NewItemSlotNode?.GetItemStack()?.GetItem();
var item = selectedItemSlotChangeEvent.NewItemSlotNode?.GetItem();
GameSceneNodeHolder.HideBackpackUiContainerIfVisible();
if (item is Node2D node2D)
{

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using ColdMint.scripts.item;
using ColdMint.scripts.item.itemStacks;
using ColdMint.scripts.map.events;
using Godot;
@ -45,27 +44,6 @@ public interface IItemContainer : IEnumerable<ItemSlotNode>
/// </summary>
public bool SupportSelect { get; set; }
/// <summary>
/// <para>Determines the number of items that can be received from the specified pile</para>
/// <para>判断能从指定物品堆中接收的物品数量</para>
/// </summary>
/// <param name="itemStack">
/// <para>Item stack to add to the current container</para>
/// <para>向该容器中放入物品的物品堆</para>
/// </param>
/// <returns></returns>
int CanAddItemStack(IItemStack itemStack);
/// <summary>
/// <para>Add an stack of items to this container</para>
/// <para>向当前容器中存入一堆物品</para>
/// </summary>
/// <param name="itemStack"></param>
/// <returns>
/// <para>If the source item stack is empty after the operation is completed</para>
/// <para>操作完成后,源物品堆是否被取空</para>
/// </returns>
bool AddItemStack(IItemStack itemStack);
/// <summary>
/// <para>Gets the selected location</para>
@ -81,23 +59,8 @@ public interface IItemContainer : IEnumerable<ItemSlotNode>
/// <returns></returns>
ItemSlotNode? GetSelectItemSlotNode();
/// <summary>
/// <para>If present, remove an item from the slot at the currently selected location and return it.</para>
/// <para>如果存在,移除当前选中位置的槽位中的一个物品并将其返回</para>
/// </summary>
/// <seealso cref="PickItemFromItemSlot"/><seealso cref="PickItemsFromItemSlotBySelectIndex"/>
IItem? PickItemFromItemSlotBySelectIndex();
/// <summary>
/// <para>Remove the specified number of items from the item slot at the currently selected location, and return them as a new item stack</para>
/// <para>取出当前选中位置的物品槽中指定数量的物品,并作为新的物品堆返回</para>
/// </summary>
/// <param name="value">
/// <para>Quantity to be taken out, inputs below zero represent all items</para>
/// <para>要取出的数量小于0的输入代表全部物品</para>
/// </param>
/// <seealso cref="PickItemsFromItemSlot"/><seealso cref="PickItemFromItemSlotBySelectIndex"/>
IItemStack? PickItemsFromItemSlotBySelectIndex(int value);
/// <summary>
/// <para>Removes an item from the inventory at the currently selected location</para>
@ -111,10 +74,6 @@ public interface IItemContainer : IEnumerable<ItemSlotNode>
/// <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>
/// </returns>
/// <remarks>
/// <para>Will remove the removed items from the game, if that is not the intent, consider using the <see cref="PickItemsFromItemSlotBySelectIndex"/></para>
/// <para>会将移除的物品从游戏中删除,如果目的并非如此,请考虑使用<see cref="PickItemsFromItemSlotBySelectIndex"/></para>
/// </remarks>
int RemoveItemFromItemSlotBySelectIndex(int number);
/// <summary>
@ -140,24 +99,7 @@ public interface IItemContainer : IEnumerable<ItemSlotNode>
/// <returns></returns>
ItemSlotNode? this[int index] => GetItemSlotNode(index);
/// <summary>
/// <para>If present, remove an item from the slot in the specified location and return it.</para>
/// <para>如果存在,移除指定位置的槽位中的一个物品并将其返回</para>
/// </summary>
/// <seealso cref="PickItemsFromItemSlot"/>
IItem? PickItemFromItemSlot(int itemSlotIndex);
/// <summary>
/// <para>Remove the specified number of items from the item slot in the specified location, and return them as a new item stack</para>
/// <para>取出指定位置的物品槽中指定数量的物品,并作为新的物品堆返回</para>
/// </summary>
/// <param name="itemSlotIndex"></param>
/// <param name="value">
/// <para>Quantity to be taken out, inputs below zero represent all items</para>
/// <para>要取出的数量小于0的输入代表全部物品</para>
/// </param>
/// <seealso cref="PickItemFromItemSlot"/>
IItemStack? PickItemsFromItemSlot(int itemSlotIndex, int value);
/// <summary>
/// <para>Removes an item from the item slot in the specified location</para>
@ -172,10 +114,6 @@ public interface IItemContainer : IEnumerable<ItemSlotNode>
/// <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>
/// </returns>
/// <remarks>
/// <para>Will remove the removed items from the game, if that is not the intent, consider using the <see cref="PickItemsFromItemSlot"/></para>
/// <para>会将移除的物品从游戏中删除,如果目的并非如此,请考虑使用<see cref="PickItemsFromItemSlot"/></para>
/// </remarks>
int RemoveItemFromItemSlot(int itemSlotIndex, int number);
/// <summary>
@ -189,43 +127,6 @@ public interface IItemContainer : IEnumerable<ItemSlotNode>
/// </returns>
ItemSlotNode? Match(IItem item);
/// <summary>
/// <para>Based on the given item stack, match the item slots where it can be added to</para>
/// <para>根据给定的物品堆,匹配可放置它的物品槽</para>
/// </summary>
/// <param name="stack"></param>
/// <returns>
/// <para>Return null if there is no slot to add the item slot in</para>
/// <para>若没有槽可放置此物品堆则返回null</para>
/// </returns>
ItemSlotNode? Match(IItemStack stack);
/// <summary>
/// <para>Match the first item slot that satisfies the predicate</para>
/// <para>匹配首个拥有满足指定条件的物品槽</para>
/// </summary>
/// <param name="predicate">
///<para>predicate</para>
///<para>谓语</para>
/// </param>
/// <returns>
/// <para>Return null if there is no slot satisfies the predicate</para>
/// <para>若没有满足条件的槽位返回null</para>
/// </returns>
/// <seealso cref="MatchAll"/>
ItemSlotNode? Match(Func<ItemSlotNode, bool> predicate);
/// <summary>
/// <para>Match all item slots that satisfies the predicate</para>
/// <para>匹配所有拥有满足指定条件的物品槽</para>
/// </summary>
/// <param name="predicate"></param>
/// <returns>
/// <para>IEnumerable for the item slot matched to, will be empty if there's no slot satisfies the predicate</para>
/// <para>包含匹配到的槽位的IEnumerable当没有满足条件的槽位时为空</para>
/// </returns>
IEnumerable<ItemSlotNode> MatchAll(Func<ItemSlotNode, bool> predicate);
/// <summary>
/// <para>AddItemSlot</para>

View File

@ -1,5 +1,6 @@
using System;
using System.Text.RegularExpressions;
using ColdMint.scripts.item;
using ColdMint.scripts.item.itemStacks;
using ColdMint.scripts.utils;
using Godot;
@ -11,7 +12,6 @@ namespace ColdMint.scripts.inventory;
/// </summary>
public partial class ItemSlotNode : MarginContainer
{
private IItemStack? _itemStack;
private TextureRect? _backgroundTextureRect;
private TextureRect? _iconTextureRect;
private Label? _quantityLabel;
@ -19,6 +19,7 @@ public partial class ItemSlotNode : MarginContainer
private bool _isSelect;
private Texture2D? _backgroundTexture;
private Texture2D? _backgroundTextureWhenSelect;
private IItem? _item = null;
public override void _Ready()
{
@ -34,7 +35,7 @@ public partial class ItemSlotNode : MarginContainer
public override Variant _GetDragData(Vector2 atPosition)
{
if (_isSelect || _iconTextureRect == null || _itemStack == null)
if (_isSelect || _iconTextureRect == null)
{
//Drag is not allowed if there is no icon or no pile of items.
//如果没有图标或者没有物品堆,那么不允许拖动。
@ -67,15 +68,20 @@ public partial class ItemSlotNode : MarginContainer
}
var itemSlotNode = data.As<ItemSlotNode>();
var itemStack = itemSlotNode.GetItemStack();
if (itemStack == null)
var item = itemSlotNode.GetItem();
if (item == null)
{
//Return null when trying to get the source item heap.
//尝试获取源物品时返回null。
//Return null when trying to get the source item.
//尝试获取源物品时返回null。
return false;
}
return _itemStack == null;
return _item == null;
}
public IItem? GetItem()
{
return _item;
}
public override void _DropData(Vector2 atPosition, Variant data)
@ -94,7 +100,7 @@ public partial class ItemSlotNode : MarginContainer
}
var itemSlotNode = data.As<ItemSlotNode>();
var itemStack = itemSlotNode.ReplaceItemStack(null);
var itemStack = itemSlotNode._item;
if (itemStack == null)
{
//Return null when trying to get the source item heap.
@ -102,7 +108,7 @@ public partial class ItemSlotNode : MarginContainer
return;
}
ReplaceItemStack(itemStack);
// ReplaceItemStack(itemStack);
}
public bool IsSelect
@ -121,212 +127,22 @@ public partial class ItemSlotNode : MarginContainer
public TextureRect? BackgroundTextureRect => _backgroundTextureRect;
public bool IsEmpty() => _itemStack == null;
/// <summary>
/// <para>Get the item stack in the item slot</para>
/// <para>获取物品槽内的物品堆</para>
/// </summary>
/// <returns></returns>
public IItemStack? GetItemStack() => _itemStack;
/// <summary>
/// <para>If present, remove an item in this slot and return it.</para>
/// <para>如果存在,移除该槽位中的一个物品并将其返回</para>
/// </summary>
/// <seealso cref="PickItems"/>
public IItem? PickItem()
{
if (_itemStack is null) return null;
var result = _itemStack.PickItem();
if (_itemStack.Empty)
{
SetItemStack(null);
}
UpdateAllDisplay();
return result;
}
/// <summary>
/// <para>Remove the specified number of items and return them as a new item stack</para>
/// <para>取出当前物品槽中指定数量的物品,并作为新的物品堆返回</para>
/// </summary>
/// <param name="value">
/// <para>Quantity to be taken out, inputs below zero represent all items</para>
/// <para>要取出的数量小于0的输入代表全部物品</para>
/// </param>
/// <seealso cref="PickItem"/>
public IItemStack? PickItems(int value)
{
if (_itemStack is null) return null;
var result = _itemStack.PickItems(value);
if (_itemStack.Empty)
{
SetItemStack(null);
}
UpdateAllDisplay();
return result;
}
/// <summary>
/// <para>Removes the specified number of items from the item slot</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>
/// </returns>
/// <remarks>
/// <para>Will remove the removed items from the game, if that is not the intent, consider using the <see cref="PickItems"/></para>
/// <para>会将移除的物品从游戏中删除,如果目的并非如此,请考虑使用<see cref="PickItems"/></para>
/// </remarks>
public int RemoveItem(int number)
{
if (_itemStack == null)
{
return number;
}
var result = _itemStack.RemoveItem(number);
//If the specified number of items is removed, the number of items is less than or equal to 0. Then we empty the inventory.
//如果移除指定数量的物品后物品数量小于或等于0。那么我们清空物品栏。
if (_itemStack.Empty)
{
SetItemStack(null);
}
UpdateAllDisplay();
return result;
}
/// <summary>
/// <para>Remove item stack from slot and return it, equivalent to ReplaceItemStack(null)</para>
/// <para>从当前槽位中移出并返回物品堆等价于ReplaceItemStack(null)</para>
/// <seealso cref="ReplaceItemStack"/>
/// </summary>
public IItemStack? RemoveItemStack() => ReplaceItemStack(null);
/// <summary>
/// <para>Empty the item slot</para>
/// <para>清空当前物品槽</para>
/// </summary>
/// <remarks>
///<para>This method will remove all items stored in the item slots from the game, if this is not what you want to do, consider using the <see cref="RemoveItemStack"/> method.</para>
///<para>此方法会从游戏中移除储存于物品槽中的所有物品,若这不是您希望的操作,请考虑使用<see cref="RemoveItemStack"/>方法。</para>
/// </remarks>
public void ClearSlot()
{
_itemStack?.ClearStack();
SetItemStack(null);
UpdateAllDisplay();
}
/// <summary>
/// <para>
/// Set item stack for this slot, this will completely replace current item stack.
/// If you want the item stack to be added to current stack, use the <see cref="AddItemStack"/>.
/// </para>
/// <para>为物品槽设置物品堆,将完全替换掉当前物品堆。如果想要物品堆叠加至该物品堆,请使用<see cref="AddItemStack"/></para>
/// <para>Whether the item in this node is empty</para>
/// <para>此节点内的物品是否为空的</para>
/// </summary>
/// <returns>
/// <para>The item stack that was previously in this slot</para>
/// <para>该槽位中原本的物品堆</para>
///<para>Return true if the number of items is 0 or the item object does not exist</para>
///<para>当物品数量为0或物品对象不存在时返回true</para>
/// </returns>
public IItemStack? ReplaceItemStack(IItemStack? newItemStack)
public bool IsEmpty()
{
var result = _itemStack;
SetItemStack(newItemStack);
UpdateAllDisplay();
return result;
}
/// <summary>
/// <para>Can the specified item be placed in the item slot?</para>
/// <para>指定的物品是否可设置在物品槽内?</para>
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public bool CanAddItem(IItem item)
{
if (_itemStack == null) return true;
return _itemStack.CanAddItem(item);
}
/// <summary>
/// <para>Try to add an item to this slot, if it can't be added to this slot, return false</para>
/// <para>尝试向当前槽位中加入物品如果该物品不能被放入该槽位返回false</para>
/// </summary>
public bool AddItem(IItem item)
{
bool result;
if (_itemStack is null)
if (_item == null || _item.Quantity == 0)
{
SetItemStack(IItemStack.FromItem(item));
result = true;
}
else
{
result = _itemStack.AddItem(item);
return true;
}
if (result)
{
UpdateAllDisplay();
}
return result;
}
/// <summary>
/// <para>Determines the number of items that can be received from the specified pile</para>
/// <para>判断能从指定物品堆中接收的物品数量</para>
/// </summary>
/// <param name="itemStack">
/// <para>Item stack to add to the current slot</para>
/// <para>向该物品槽中放入物品的物品堆</para>
/// </param>
/// <returns></returns>
public int CanAddItemStack(IItemStack itemStack)
{
if (_itemStack is null) return itemStack.Quantity;
return _itemStack.CanTakeFrom(itemStack);
}
/// <summary>
/// <para>Try to combine an item stack into this slot</para>
/// <para>尝试将一个物品堆合并至该槽位中</para>
/// </summary>
/// <returns>
/// <para>If the source item stack is empty after the operation is completed</para>
/// <para>操作完成后,源物品堆是否被取空</para>
/// </returns>
public bool AddItemStack(IItemStack itemStack)
{
bool result;
if (_itemStack is null)
{
SetItemStack(itemStack);
result = false;
}
else
{
result = _itemStack.TakeFrom(itemStack);
}
UpdateAllDisplay();
return result;
return false;
}
@ -347,7 +163,7 @@ public partial class ItemSlotNode : MarginContainer
/// </summary>
private void UpdateTooltipText()
{
if (_itemStack == null)
if (_item == null)
{
TooltipText = null;
return;
@ -358,16 +174,16 @@ public partial class ItemSlotNode : MarginContainer
var debugText = TranslationServerUtils.Translate("item_prompt_debug");
if (debugText != null)
{
TooltipText = string.Format(debugText, _itemStack.GetItem()?.Id,
TranslationServerUtils.Translate(_itemStack.Name),
_itemStack.Quantity, _itemStack.MaxQuantity, _itemStack.GetType().Name,
TranslationServerUtils.Translate(_itemStack.Description));
TooltipText = string.Format(debugText, _item.Id,
TranslationServerUtils.Translate(_item.Name),
_item.Quantity, _item.MaxQuantity, _item.GetType().Name,
TranslationServerUtils.Translate(_item.Description));
}
}
else
{
TooltipText = TranslationServerUtils.Translate(_itemStack.Name) + "\n" +
TranslationServerUtils.Translate(_itemStack.Description);
TooltipText = TranslationServerUtils.Translate(_item.Name) + "\n" +
TranslationServerUtils.Translate(_item.Description);
}
}
@ -382,7 +198,7 @@ public partial class ItemSlotNode : MarginContainer
return;
}
switch (_itemStack?.Quantity)
switch (_item?.Quantity)
{
case null or 1:
_quantityLabel.Hide();
@ -390,25 +206,12 @@ public partial class ItemSlotNode : MarginContainer
default:
//When the quantity is not null or 1, we display the quantity.
//当数量不为null或1时我们显示数量
_quantityLabel.Text = _itemStack?.Quantity.ToString();
_quantityLabel.Text = _item?.Quantity.ToString();
_quantityLabel.Show();
break;
}
}
/// <summary>
/// <para>SetItemStack</para>
/// <para>设置物品堆</para>
/// </summary>
/// <remarks>
///<para>This method broadcasts changes to the stack to the outside world</para>
///<para>此方法会对外广播物品堆的变更事件</para>
/// </remarks>
/// <param name="itemStack"></param>
private void SetItemStack(IItemStack? itemStack)
{
_itemStack = itemStack;
}
/// <summary>
/// <para>Update texture of the icon rect</para>
@ -418,7 +221,72 @@ public partial class ItemSlotNode : MarginContainer
{
if (_iconTextureRect != null)
{
_iconTextureRect.Texture = _itemStack?.Icon;
_iconTextureRect.Texture = _item?.Icon;
}
}
public bool CanAddItem(IItem item)
{
if (_item == null)
{
//If there is no item in the current item slot, it is allowed to add.
//如果当前物品槽内没物品,那么允许添加。
return true;
}
if (item.Id != _item.Id)
{
//If the item ID you want to add is different from the current item ID, disable.
//如果要添加的物品ID和当前的物品ID不一样那么禁止。
return false;
}
var newQuantity = item.Quantity + _item.Quantity;
if (newQuantity > _item.MaxQuantity)
{
//The maximum number is exceeded and items cannot be added.
//超过了最大数量,无法添加物品。
return false;
}
return true;
}
/// <summary>
/// <para>Remove items from the item slot</para>
/// <para>从物品槽内移除物品</para>
/// </summary>
/// <param name="number">
///<para>number(Less than 0, remove all items)</para>
///<para>物品数量(小于0则移除全部物品)</para>
/// </param>
/// <returns>
///<para>How many items were actually removed</para>
///<para>实际移除了多少个物品</para>
/// </returns>
public int RemoveItem(int number)
{
if (_item == null)
{
return 0;
}
//The number of actual removals
//实际移除的数量
var removeNumber = number < 0 ? _item.Quantity : number;
_item.Quantity -= removeNumber;
return removeNumber;
}
public bool AddItem(IItem item)
{
if (_item == null)
{
return false;
}
var newQuantity = item.Quantity + _item.Quantity;
_item.Quantity = Math.Min(newQuantity, _item.MaxQuantity);
return true;
}
}

View File

@ -3,7 +3,6 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ColdMint.scripts.item;
using ColdMint.scripts.item.itemStacks;
using ColdMint.scripts.map.events;
using ColdMint.scripts.utils;
using Godot;
@ -17,10 +16,9 @@ namespace ColdMint.scripts.inventory;
/// </summary>
public class UniversalItemContainer : IItemContainer
{
private readonly PackedScene? _itemSlotPackedScene = GD.Load<PackedScene>("res://prefab/ui/ItemSlot.tscn");
private readonly List<ItemSlotNode>? _itemSlotNodes = [];
private readonly PackedScene? _itemSlotPackedScene = GD.Load<PackedScene>("res://prefab/ui/ItemSlot.tscn");
/// <summary>
/// <para>UnknownIndex</para>
@ -31,8 +29,17 @@ public class UniversalItemContainer : IItemContainer
//_selectIndex默认为0.
private int _selectIndex;
[MustDisposeResource]
public IEnumerator<ItemSlotNode> GetEnumerator()
{
return _itemSlotNodes?.GetEnumerator() ?? Enumerable.Empty<ItemSlotNode>().GetEnumerator();
}
public bool SupportSelect { get; set; } = true;
[MustDisposeResource]
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public Action<SelectedItemSlotChangeEvent>? SelectedItemSlotChangeEvent { get; set; }
@ -52,43 +59,16 @@ public class UniversalItemContainer : IItemContainer
return itemSlotNode.AddItem(item);
}
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,
slots.Select(slot => slot.CanAddItemStack(itemStack)).Sum());
}
public bool AddItemStack(IItemStack itemStack)
{
ItemSlotNode? itemSlotNode = Match(itemStack);
while (itemSlotNode is not null)
{
if (itemSlotNode.AddItemStack(itemStack))
return true;
itemSlotNode = Match(itemStack);
}
return false;
}
public bool SupportSelect { get; set; }
public int GetSelectIndex()
{
if (!SupportSelect)
{
return 0;
}
return _selectIndex;
}
public ItemSlotNode? GetSelectItemSlotNode()
{
if (!SupportSelect || _itemSlotNodes == null || _itemSlotNodes.Count == 0)
if (_itemSlotNodes == null || _itemSlotNodes.Count == 0)
{
return null;
}
@ -103,35 +83,7 @@ public class UniversalItemContainer : IItemContainer
return null;
}
public IItem? PickItemFromItemSlotBySelectIndex()
{
if (!SupportSelect)
{
return null;
}
return PickItemFromItemSlot(_selectIndex);
}
public IItemStack? PickItemsFromItemSlotBySelectIndex(int value)
{
if (!SupportSelect)
{
return null;
}
return PickItemsFromItemSlot(_selectIndex, value);
}
public int RemoveItemFromItemSlotBySelectIndex(int number)
{
if (!SupportSelect)
{
return 0;
}
return RemoveItemFromItemSlot(_selectIndex, number);
}
public int RemoveItemFromItemSlotBySelectIndex(int number) => RemoveItemFromItemSlot(_selectIndex, number);
public int GetItemSlotCount()
{
@ -154,32 +106,6 @@ public class UniversalItemContainer : IItemContainer
return _itemSlotNodes[safeIndex];
}
public IItem? PickItemFromItemSlot(int itemSlotIndex)
{
if (_itemSlotNodes == null) return null;
var safeIndex = GetSafeIndex(itemSlotIndex);
if (safeIndex == UnknownIndex)
{
return null;
}
var itemSlot = _itemSlotNodes[safeIndex];
return itemSlot.PickItem();
}
public IItemStack? PickItemsFromItemSlot(int itemSlotIndex, int value)
{
if (_itemSlotNodes == null) return null;
var safeIndex = GetSafeIndex(itemSlotIndex);
if (safeIndex == UnknownIndex)
{
return null;
}
var itemSlot = _itemSlotNodes[safeIndex];
return itemSlot.PickItems(value);
}
public int RemoveItemFromItemSlot(int itemSlotIndex, int number)
{
if (_itemSlotNodes == null) return number;
@ -193,30 +119,6 @@ public class UniversalItemContainer : IItemContainer
return itemSlot.RemoveItem(number);
}
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? Match(IItemStack stack)
{
var item = stack.GetItem();
return item == null ? null : _itemSlotNodes?.FirstOrDefault(itemSlotNode => itemSlotNode.CanAddItem(item));
}
public ItemSlotNode? Match(Func<ItemSlotNode, bool> predicate)
{
return _itemSlotNodes?.FirstOrDefault(predicate);
}
public IEnumerable<ItemSlotNode> MatchAll(Func<ItemSlotNode, bool> predicate)
{
return from node in _itemSlotNodes where predicate(node) select node;
}
/// <summary>
/// <para>Gets a secure subscript index</para>
/// <para>获取安全的下标索引</para>
@ -244,10 +146,13 @@ public class UniversalItemContainer : IItemContainer
return itemSlotIndex % count;
}
/// <summary>
/// <para>Add items tank</para>
/// <para>添加物品槽</para>
/// </summary>
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)
@ -261,22 +166,14 @@ public class UniversalItemContainer : IItemContainer
return null;
}
if (SupportSelect)
{
itemSlotNode.IsSelect = (_itemSlotNodes.Count) == _selectIndex;
}
else
{
itemSlotNode.IsSelect = false;
}
itemSlotNode.IsSelect = (_itemSlotNodes.Count) == _selectIndex;
_itemSlotNodes.Add(itemSlotNode);
return itemSlotNode;
}
public void SelectTheNextItemSlot()
{
if (!SupportSelect || _itemSlotNodes == null)
if (_itemSlotNodes == null)
{
return;
}
@ -299,7 +196,7 @@ public class UniversalItemContainer : IItemContainer
public void SelectThePreviousItemSlot()
{
if (!SupportSelect || _itemSlotNodes == null)
if (_itemSlotNodes == null)
{
return;
}
@ -320,22 +217,6 @@ public class UniversalItemContainer : IItemContainer
PrivateSelectItemSlot(oldSelectIndex, newSelectIndex);
}
public void SelectItemSlot(int newSelectIndex)
{
if (!SupportSelect || newSelectIndex == _selectIndex)
{
return;
}
var safeIndex = GetSafeIndex(newSelectIndex);
if (safeIndex == UnknownIndex)
{
return;
}
PrivateSelectItemSlot(_selectIndex, newSelectIndex);
}
/// <summary>
/// <para>Select an item slot</para>
/// <para>选中某个物品槽</para>
@ -368,7 +249,7 @@ public class UniversalItemContainer : IItemContainer
/// <param name="index"></param>
private void HideItem(int index)
{
var oldItem = _itemSlotNodes?[index].GetItemStack()?.GetItem();
var oldItem = _itemSlotNodes?[index].GetItem();
if (oldItem is not Node2D oldNode2D) return;
oldNode2D.ProcessMode = Node.ProcessModeEnum.Disabled;
oldNode2D.Hide();
@ -385,22 +266,25 @@ public class UniversalItemContainer : IItemContainer
/// <param name="index"></param>
private void DisplayItem(int index)
{
var item = _itemSlotNodes?[index].GetItemStack()?.GetItem();
var item = _itemSlotNodes?[index].GetItem();
if (item is not Node2D newNode2D) return;
newNode2D.ProcessMode = Node.ProcessModeEnum.Inherit;
newNode2D.Show();
}
[MustDisposeResource]
public IEnumerator<ItemSlotNode> GetEnumerator()
public void SelectItemSlot(int newSelectIndex)
{
return _itemSlotNodes?.GetEnumerator() ?? Enumerable.Empty<ItemSlotNode>().GetEnumerator();
}
if (newSelectIndex == _selectIndex)
{
return;
}
[MustDisposeResource]
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
var safeIndex = GetSafeIndex(newSelectIndex);
if (safeIndex == UnknownIndex)
{
return;
}
PrivateSelectItemSlot(_selectIndex, newSelectIndex);
}
}

View File

@ -1,24 +0,0 @@
using ColdMint.scripts.item.itemStacks;
namespace ColdMint.scripts.item;
/// <summary>
/// <para>The special item interface makes the item a normal item, in other words, will be stacked in<see cref="CommonItemStack"/></para>
/// <para>该特殊的物品接口使得物品成为普通的物品,换言之,将会堆叠在<see cref="CommonItemStack"/>中。</para>
/// </summary>
/// <remarks>
/// <para>
/// Notice when you implement: To avoid unexpected behavior, unless you understand what you're doing, the <see cref="IItem.CanStackWith"/> method
/// of an item that implements the interface must only match its own exact same instance.
/// </para>
/// <para>实现时注意:为避免意外行为,除非你明白自己在做什么,否则实现接口的物品的<see cref="IItem.CanStackWith"/>方法必须只和自己完全相同的实例匹配。</para>
/// </remarks>
public interface ICommonItem : IItem
{
/// <summary>
/// <para>Method to clone an instance same with self. Will be used to pick out item instance from a <see cref="CommonItemStack"/></para>
/// <para>复制与自身相同的实例的方法。将用于从 <see cref="CommonItemStack"/> 中拿取新的物品实例。</para>
/// </summary>
/// <returns></returns>
ICommonItem CloneInstance();
}

View File

@ -1,6 +1,4 @@
using ColdMint.scripts.item.itemStacks;
using Godot;
using Godot;
namespace ColdMint.scripts.item;
@ -11,29 +9,36 @@ public interface IItem
/// <para>当前项目的ID</para>
/// </summary>
string Id { get; }
/// <summary>
/// <para>Icon of current item</para>
/// <para>当前项目的图标</para>
/// </summary>
Texture2D Icon { get; }
/// <summary>
/// <para>Display name of current item</para>
/// <para>显示当前Item的名称</para>
/// </summary>
string Name { get; }
/// <summary>
/// <para>Description of current item, which may show in inventory</para>
/// <para>当前项目的描述</para>
/// </summary>
string? Description { get; }
/// <summary>
/// <para>
/// Whether the current item can be put into item containers like packsack.<br/>
/// This attribute is usually set to false for items of the backpack class to avoid pack nesting.
/// </para>
/// <para>当前物品是否可以放入背包类的容器中。一般将背包类物品的该属性设为false来避免背包嵌套。</para>
/// <para>Quantity</para>
/// <para>当前的数量</para>
/// </summary>
bool CanPutInPack { get; }
int Quantity { get; set; }
/// <summary>
/// <para>MaxItemQuantity</para>
/// <para>最大物品数量</para>
/// </summary>
int MaxQuantity { get; }
/// <summary>
/// <para>Execute when current item is used <br/> e.g. when player clicks left mouse button with current item in hand</para>
@ -42,43 +47,4 @@ public interface IItem
/// <param name="owner">Owner of current item, if any</param>
/// <param name="targetGlobalPosition">Target position, such as the position of the cursor when used by the player</param>
void Use(Node2D? owner, Vector2 targetGlobalPosition);
/// <summary>
/// <para>Execute when current item be removed from game.</para>
/// <para>当前物品从游戏中移除时执行。</para>
/// </summary>
void Destroy();
/// <summary>
/// <para>Return true if this item can be stacked with the given item in one stack</para>
/// <para>若该物品是否能与给定物品堆叠在同一个物品堆上返回true</para>
/// </summary>
/// <remarks>
/// <para>
/// ! Note in the implementation: the correspondence of this predicate must be an equivalence relation over the full set of stackable items
/// (i.e., be able to derive a division into the full set of stackable items).<br/>
/// Or if the item is not stackable, make it always return false.
/// </para>
/// <para>
/// !实现时注意:该谓词的对应关系必须是在全部可堆叠的物品集合上的等价关系(即能导出一个对可堆叠物品全集的划分)。<br/>
/// 或如果该物品不可堆叠令其永远返回false。<br/>
/// </para>
/// <para>
/// If it is necessary to implement special stacking relationships (e.g. containers that can be stacked to hold items),
/// customize the special <see cref="IItemStack"/> type, implement the special stacking determination in it by overriding <see cref="IItemStack.CanAddItem"/>,
/// and override the <see cref="SpecialStack"/> method so that it returns an instance of that custom ItemStack.
/// </para>
/// <para>
/// 如果有必要实现特殊的堆叠关系(如可以用堆叠来收纳物品的容器),请自定义特殊的<see cref="IItemStack"/>类型,在其中重写<see cref="IItemStack.CanAddItem"/>以实现特殊的堆叠判定,
/// 并重写<see cref="SpecialStack"/>方法使其返回该自定义ItemStack实例。
/// </para>
/// </remarks>
bool CanStackWith(IItem item);
/// <summary>
/// <para>If this item need a special stack type, return the special item stack instance that contains the item. If else, just leave this null.</para>
/// <para>如果该项目需要特殊的物品堆类型重写此方法来返回包含该物品的特殊物品堆实例。否则保留原本的null返回值。</para>
/// </summary>
/// <seealso cref="CanStackWith"/>
IItemStack? SpecialStack() => null;
}

View File

@ -120,9 +120,7 @@ public static class ItemTypeRegister
string ScenePath,
string IconPath,
int MaxStackValue,
IList<CustomArg>? CustomArgs)
{
}
IList<CustomArg>? CustomArgs);
private readonly record struct CustomArg(string Name, CustomArgType Type, string Value)
{

View File

@ -20,16 +20,6 @@ public partial class Packsack : PickAbleTemplate
public override bool CanPutInPack => false;
public override void Destroy()
{
if (ItemContainer == null) return;
foreach (var itemSlot in ItemContainer)
{
itemSlot.ClearSlot();
}
QueueFree();
}
public override void Use(Node2D? owner, Vector2 targetGlobalPosition)
{

View File

@ -1,108 +0,0 @@
using System;
using Godot;
namespace ColdMint.scripts.item.itemStacks;
/// <summary>
/// <para>An ordinary item pile, in which only one instance of the item is actually saved.</para>
/// <para>普通的物品堆,堆中实际保存的物品实例仅有一个。</para>
/// </summary>
/// <remarks>
///<para>When the <see cref="AddItem"/> method is called in this implementation, the number of internal items is increased by one and new items passed in are destroyed.</para>
///<para>在此实现下调用<see cref="AddItem"/>方法时,会对内部物品的数量加一,并销毁传递进来的新物品。</para>
/// </remarks>
/// <param name="innerItem">
///<para>innerItem</para>
///<para>内部物品</para>
/// </param>
/// <seealso cref="UniqueItemStack"/>
/// <seealso cref="SingleItemStack"/>
public class CommonItemStack(ICommonItem innerItem) : IItemStack
{
public int MaxQuantity { get; } = ItemTypeManager.MaxStackQuantityOf(innerItem.Id);
public int Quantity { get; private set; } = 1;
public bool Empty => Quantity == 0;
public Texture2D Icon => innerItem.Icon;
public string Name => $"{innerItem.Name}({Quantity})";
public string? Description => innerItem.Description;
public bool CanAddItem(IItem item1)
{
return innerItem.CanStackWith(item1) && (Quantity < MaxQuantity);
}
public bool AddItem(IItem item)
{
if (!CanAddItem(item)) return false;
Quantity++;
item.Destroy();
return true;
}
public int CanTakeFrom(IItemStack itemStack)
{
var item = itemStack.GetItem();
if (item == null)
{
return 0;
}
if (itemStack.Empty || !innerItem.CanStackWith(item)) return 0;
return Math.Min(itemStack.Quantity, MaxQuantity - Quantity);
}
public bool TakeFrom(IItemStack itemStack)
{
var number = CanTakeFrom(itemStack);
itemStack.RemoveItem(number);
Quantity += number;
return itemStack.Empty;
}
public IItem? GetItem()
{
return Empty ? null : innerItem;
}
public IItem? PickItem()
{
if(Empty) return null;
Quantity--;
var result = innerItem.CloneInstance();
if(Empty) innerItem.Destroy();
return result;
}
public IItemStack? PickItems(int value)
{
if (Empty) return null;
var result = new CommonItemStack(innerItem.CloneInstance());
var n = Math.Min(Quantity, value);
if (n < 0)
{
n = Quantity;
}
result.Quantity = n;
Quantity -= n;
if (Empty) innerItem.Destroy();
return result;
}
public int RemoveItem(int number)
{
var n = Math.Min(number, Quantity);
if (n < 0)
{
n = Quantity;
}
Quantity -= n;
if (Empty) innerItem.Destroy();
return number - n;
}
public void ClearStack()
{
if (Empty) return;
Quantity = 0;
innerItem.Destroy();
}
}

View File

@ -1,175 +0,0 @@
using System;
using Godot;
namespace ColdMint.scripts.item.itemStacks;
/// <summary>
/// <para>物品槽中的物品堆</para>
/// <para>Item stack in an inventory slot</para>
/// </summary>
public interface IItemStack
{
/// <summary>
/// <para>Max number of current stack</para>
/// <para>当前物品堆的最大物品数量</para>
/// </summary>
int MaxQuantity { get; }
/// <summary>
/// <para>Quantity of current stack</para>
/// <para>当前物品堆的物品数量</para>
/// </summary>
int Quantity { get; }
/// <summary>
/// <para>True if this stack is empty</para>
/// <para>当物品堆空时为真</para>
/// </summary>
/// <remarks>
/// <para>
/// This attribute is used to check if the item stack is empty after the operation for subsequent processing,<br/>
/// i.e. there should not be any item stacks with this attribute true outside of the operation process
/// </para>
/// <para>此属性用于检查操作后该物品堆是否为空以便后续处理也就是说在操作过程以外的时候不应当存在任何该属性为true的物品堆</para>
/// </remarks>
bool Empty { get; }
/// <summary>
/// <para>Icon of current item stack</para>
/// <para>当前物品堆显示的图标</para>
/// </summary>
Texture2D Icon { get; }
/// <summary>
/// <para>Display name of current item stack</para>
/// <para>当前物品堆显示的名称</para>
/// </summary>
string Name { get; }
/// <summary>
/// <para>Description of current item stack, which may show in inventory</para>
/// <para>当前物品堆的描述,可能显示在物品栏中</para>
/// </summary>
string? Description { get; }
/// <summary>
/// <para>Determine whether a specified item can be accommodated</para>
/// <para>判断能否容纳指定物品</para>
/// </summary>
/// <returns></returns>
public bool CanAddItem(IItem item);
/// <summary>
/// <para>Add items to the itemStack</para>
/// <para>添加物品到物品堆内</para>
/// </summary>
/// <param name="item">
///<para>Items to add</para>
///<para>需要添加的物品</para>
/// </param>
/// <returns>
///<para>Whether successful</para>
///<para>是否成功</para>
/// </returns>
public bool AddItem(IItem item);
/// <summary>
/// <para>Determines the number of items that can be received from the specified pile</para>
/// <para>判断能从指定物品堆中接收的物品数量</para>
/// </summary>
/// <param name="itemStack">
/// <para>Item stack to add to the current stack</para>
/// <para>向该物品堆中放入物品的物品堆</para>
/// </param>
/// <returns></returns>
public int CanTakeFrom(IItemStack itemStack);
/// <summary>
/// <para>Move as many items as possible from the specified item pile to the current item pile. Items that have been moved to the current item pile should be removed from the original item pile.</para>
/// <para>将指定物品堆中尽可能多的物品移动至当前物品堆中,被移入当前堆的物品应从原物品堆中移除。</para>
/// </summary>
/// <param name="itemStack">
/// <para>The pile of items that are moved into the current pile</para>
/// <para>被移入当前堆的物品堆</para>
/// </param>
/// <returns>
/// <para>Whether the original stack is empty after the operation</para>
/// <para>操作结束后原物品堆是否为空</para>
/// </returns>
public bool TakeFrom(IItemStack itemStack);
/// <summary>
/// <para>Gets an item instance of the current item pile without retrieving the item</para>
/// <para>获取当前物品堆的物品实例而不取出该物品</para>
/// <seealso cref="PickItem"/>
/// </summary>
/// <returns></returns>
public IItem? GetItem();
/// <summary>
/// <para>Pop the item instance at the top of current item stack and return it</para>
/// <para>取出当前物品堆顶部的物品实例并返回该物品</para>
/// <seealso cref="GetItem"/><seealso cref="PickItems"/>
/// </summary>
/// <returns></returns>
public IItem? PickItem();
/// <summary>
/// <para>Remove the specified number of items and return them as a new item stack</para>
/// <para>取出当前堆中指定数量的物品,并作为新的物品堆返回</para>
/// <seealso cref="PickItem"/>
/// </summary>
/// <param name="value">
/// <para>Quantity to be taken out, inputs below zero represent all items</para>
/// <para>要取出的数量小于0的输入代表全部物品</para>
/// </param>
/// <returns>
/// <para>The item stack that is taken out, can be null if out nothing, should not be the current item stack itself</para>
/// <para>取出的物品堆没有取出物品时可为null不应是当前物品堆自身</para>
/// </returns>
public IItemStack? PickItems(int value);
/// <summary>
/// <para>
/// Removes the specified number of items from current item stack,removed items should be removed from the game<br/>
/// If you don't want remove them from game, consider <see cref="PickItem"/> and <see cref="PickItems"/>
/// </para>
/// <para>
/// 在当前物品堆移除指定数量的物品,被移除的物品应当从游戏中移除。<br/>
/// 如果您并不打算将它们从游戏中移除,请考虑使用 <see cref="PickItem"/> 和 <see cref="PickItems"/>
/// </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>
/// </returns>
public int RemoveItem(int number);
/// <summary>
/// <para>Clear current stack, which means should remove all items inside current stack from the game here</para>
/// <para>清除当前物品堆,意味着从游戏中移除当前堆中的所有物品</para>。
/// </summary>
public void ClearStack();
/// <summary>
/// <para>Create a new ItemStack with the given item as the first item</para>
/// <para>以给定的物品为第一个物品创建物品堆</para>
/// </summary>
/// <remarks>
///<para>Assuming the item implements the <see cref="IItem.SpecialStack"/> method, then use the return value of the SpecialStack method, otherwise extrapolate from the maximum number of stacks of items.</para>
///<para>假设物品实现了<see cref="IItem.SpecialStack"/>方法那么使用SpecialStack方法的返回值否则根据物品的最大堆叠数量来推断。</para>
/// </remarks>
public static IItemStack FromItem(IItem item) =>
item.SpecialStack() ??
ItemTypeManager.MaxStackQuantityOf(item.Id) switch
{
1 => new SingleItemStack(item),
> 1 => item is ICommonItem commonItem ? new CommonItemStack(commonItem) : new UniqueItemStack(item),
var other => throw new ArgumentException(
$"item {item} of type '{item.Id}' has unexpected max stack quantity {other}")
};
}

View File

@ -1,78 +0,0 @@
using System;
using Godot;
namespace ColdMint.scripts.item.itemStacks;
/// <summary>
/// <para>ItemStack for <see cref="Packsack"/> item. Can add items into pack by stack them to this stack.</para>
/// <para>用于<see cref="Packsack"/>物品的堆栈。可以将物品堆叠到此堆栈中,从而将物品添加到背包中。</para>
/// </summary>
/// <param name="packsack"></param>
public class PacksackStack(Packsack packsack) : IItemStack
{
public int MaxQuantity => 1;
public int Quantity => 1;
public bool Empty { get; private set; }
public Texture2D Icon => packsack.Icon;
public string Name => packsack.Name;
public string? Description => packsack.Description;
public bool CanAddItem(IItem item)
{
if (!item.CanPutInPack) return false;
return packsack.ItemContainer?.CanAddItem(item) ?? false;
}
public bool AddItem(IItem item)
{
if (!item.CanPutInPack) return false;
return packsack.ItemContainer?.AddItem(item) ?? false;
}
public int CanTakeFrom(IItemStack itemStack)
{
if (itemStack.GetItem() is Packsack) return 0;
return packsack.ItemContainer?.CanAddItemStack(itemStack) ?? 0;
}
public bool TakeFrom(IItemStack itemStack)
{
if (itemStack.GetItem() is Packsack) return false;
return packsack.ItemContainer?.AddItemStack(itemStack) ?? false;
}
public IItem? GetItem()
{
return Empty ? null : packsack;
}
public IItem? PickItem()
{
if (Empty) return null;
Empty = true;
return packsack;
}
public IItemStack? PickItems(int value)
{
if (Empty || value == 0) return null;
Empty = true;
return new PacksackStack(packsack);
}
public int RemoveItem(int number)
{
if (Empty || number == 0) return number;
Empty = true;
packsack.Destroy();
return Math.Max(0, number - 1);
}
public void ClearStack()
{
if (Empty) return;
packsack.Destroy();
Empty = true;
}
}

View File

@ -1,64 +0,0 @@
using System;
using Godot;
namespace ColdMint.scripts.item.itemStacks;
/// <summary>
/// <para>One of the basic item stacks, there are always one item in stack(Stack not supported)</para>
/// <para>单身狗物品堆,基础物品堆之一,堆中永远只会有一个物品(不支持堆叠)</para>
/// </summary>
/// <seealso cref="UniqueItemStack"/>
/// <seealso cref="CommonItemStack"/>
public class SingleItemStack(IItem item) : IItemStack
{
public IItem Item { get; init; } = item;
public int MaxQuantity => 1;
public int Quantity => 1;
public bool Empty { get; private set; }
public Texture2D Icon => Item.Icon;
public string Name => Item.Name;
public string? Description => Item.Description;
public bool CanAddItem(IItem item) => false;
public bool AddItem(IItem item) => false;
public int CanTakeFrom(IItemStack itemStack) => 0;
public bool TakeFrom(IItemStack itemStack) => false;
public IItem? GetItem()
{
return Empty ? null : Item;
}
public IItem? PickItem()
{
if (Empty) return null;
Empty = true;
return Item;
}
public IItemStack? PickItems(int value)
{
if (value == 0 || Empty) return null;
Empty = true;
return new SingleItemStack(Item);
}
public int RemoveItem(int number)
{
if (number == 0 || Empty) return 0;
Empty = true;
Item.Destroy();
return Math.Max(number - 1, 0);
}
public void ClearStack()
{
RemoveItem(1);
}
}

View File

@ -1,123 +0,0 @@
using System;
using System.Collections.Generic;
using Godot;
namespace ColdMint.scripts.item.itemStacks;
/// <summary>
/// <para>One of the basic item stacks, where each item in the stack maintains its original state (allowing for items that are not identical to each other)</para>
/// <para>独特物品堆,基础物品堆之一,堆中的每一个物品会保持各自原本的状态(允许互不相同的物品存在)</para>
/// </summary>
/// <seealso cref="CommonItemStack"/><seealso cref="SingleItemStack"/>
public class UniqueItemStack : IItemStack
{
private readonly Stack<IItem> _items;
public UniqueItemStack(IItem item)
{
_items = [];
_items.Push(item);
MaxQuantity = ItemTypeManager.MaxStackQuantityOf(item.Id);
}
private UniqueItemStack(UniqueItemStack from)
{
_items = from._items;
MaxQuantity = from.MaxQuantity;
Quantity = from.Quantity;
from.Quantity = 0;
}
public int MaxQuantity { get; }
public int Quantity { get; private set; } = 1;
public bool Empty => Quantity == 0;
public Texture2D Icon => GetItem()?.Icon ?? new PlaceholderTexture2D();
public string Name => $"{GetItem()?.Name}({Quantity})";
public string? Description => GetItem()?.Description;
public bool CanAddItem(IItem item)
{
//当两个物品相容且当前堆未满时我们返回true
return (GetItem()?.CanStackWith(item) ?? false) && (Quantity < MaxQuantity);
}
public bool AddItem(IItem item)
{
if (!CanAddItem(item)) return false;
_items.Push(item);
Quantity++;
return true;
}
public int CanTakeFrom(IItemStack itemStack)
{
if (!(itemStack.GetItem() is { } item)) return 0;
//如果两个物品相容,那么可以获取的数量取决于当前物品堆空位的大小和对方物品数量中较小的一方
if (CanAddItem(item))
return Mathf.Min(itemStack.Quantity, MaxQuantity - Quantity);
else return 0;
}
public bool TakeFrom(IItemStack itemStack)
{
var value = CanTakeFrom(itemStack);
for (int i = 0; i < value; i++)
{
//一个如果代码没有出错就用不到的安全检查
if (!AddItem(itemStack.PickItem()!)) break;
}
return itemStack.Empty;
}
public IItem? GetItem()
{
return Empty ? null : _items.Peek();
}
public IItem? PickItem()
{
if (Empty) return null;
Quantity--;
return _items.Pop();
}
public IItemStack? PickItems(int value)
{
if (Empty) return null;
if (value < 0) value = Quantity;
var result = new UniqueItemStack(PickItem()!);
//Calculate the amount left to take out
//计算剩余的要取出的数量
var restToMove = Math.Min(value - 1, Quantity);
for (var i = 0; i < restToMove; i++)
{
result.AddItem(PickItem()!);
}
return result;
}
public int RemoveItem(int number)
{
if (number < 0) number = Quantity;
while (number > 0 && Quantity > 0)
{
PickItem()!.Destroy();
number--;
}
return number;
}
public void ClearStack()
{
while (Quantity > 0)
{
PickItem()!.Destroy();
}
}
}

View File

@ -22,146 +22,146 @@ namespace ColdMint.scripts.loader.uiLoader;
/// </summary>
public partial class MainMenuLoader : UiLoaderTemplate
{
private Button? _startGameButton;
private Label? _copyrightLabel;
private StringBuilder? _copyrightBuilder;
private PackedScene? _gameScene;
private PackedScene? _contributor;
private PackedScene? _levelGraphEditor;
private Label? _sloganLabel;
private Label? _versionLabel;
private Button? _levelGraphEditorButton;
private LinkButton? _contributorButton;
private Button? _startGameButton;
private Label? _copyrightLabel;
private StringBuilder? _copyrightBuilder;
private PackedScene? _gameScene;
private PackedScene? _contributor;
private PackedScene? _levelGraphEditor;
private Label? _sloganLabel;
private Label? _versionLabel;
private Button? _levelGraphEditorButton;
private LinkButton? _contributorButton;
public override void InitializeData()
{
if (Config.IsDebug())
{
//Set the minimum log level to Info in debug mode.(Print all logs)
//在调试模式下将最小日志等级设置为Info。打印全部日志
LogCat.MinLogLevel = LogCat.InfoLogLevel;
}
else
{
//Disable all logs in the release version.
//在发行版禁用所有日志。
LogCat.MinLogLevel = LogCat.DisableAllLogLevel;
}
public override void InitializeData()
{
if (Config.IsDebug())
{
//Set the minimum log level to Info in debug mode.(Print all logs)
//在调试模式下将最小日志等级设置为Info。打印全部日志
LogCat.MinLogLevel = LogCat.InfoLogLevel;
}
else
{
//Disable all logs in the release version.
//在发行版禁用所有日志。
LogCat.MinLogLevel = LogCat.DisableAllLogLevel;
}
ContributorDataManager.RegisterAllContributorData();
DeathInfoGenerator.RegisterDeathInfoHandler(new SelfDeathInfoHandler());
MapGenerator.RegisterRoomInjectionProcessor(new ChanceRoomInjectionProcessor());
MapGenerator.RegisterRoomInjectionProcessor(new TimeIntervalRoomInjectorProcessor());
//Register the corresponding encoding provider to solve the problem of garbled Chinese path of the compressed package
//注册对应的编码提供程序,解决压缩包中文路径乱码问题
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
//创建游戏数据文件夹
var dataPath = Config.GetGameDataDirectory();
if (!Directory.Exists(dataPath))
{
Directory.CreateDirectory(dataPath);
}
ContributorDataManager.RegisterAllContributorData();
DeathInfoGenerator.RegisterDeathInfoHandler(new SelfDeathInfoHandler());
MapGenerator.RegisterRoomInjectionProcessor(new ChanceRoomInjectionProcessor());
MapGenerator.RegisterRoomInjectionProcessor(new TimeIntervalRoomInjectorProcessor());
//Register the corresponding encoding provider to solve the problem of garbled Chinese path of the compressed package
//注册对应的编码提供程序,解决压缩包中文路径乱码问题
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
//创建游戏数据文件夹
var dataPath = Config.GetGameDataDirectory();
if (!Directory.Exists(dataPath))
{
Directory.CreateDirectory(dataPath);
}
//Registered camp
//注册阵营
var defaultCamp = new Camp(Config.CampId.Default)
{
FriendInjury = true
};
CampManager.SetDefaultCamp(defaultCamp);
var mazoku = new Camp(Config.CampId.Mazoku);
CampManager.AddCamp(mazoku);
var aborigines = new Camp(Config.CampId.Aborigines);
CampManager.AddCamp(aborigines);
_gameScene = GD.Load<PackedScene>("res://scenes/game.tscn");
_contributor = GD.Load<PackedScene>("res://scenes/contributor.tscn");
_levelGraphEditor = GD.Load<PackedScene>("res://scenes/levelGraphEditor.tscn");
//Registered camp
//注册阵营
var defaultCamp = new Camp(Config.CampId.Default)
{
FriendInjury = true
};
CampManager.SetDefaultCamp(defaultCamp);
var mazoku = new Camp(Config.CampId.Mazoku);
CampManager.AddCamp(mazoku);
var aborigines = new Camp(Config.CampId.Aborigines);
CampManager.AddCamp(aborigines);
_gameScene = GD.Load<PackedScene>("res://scenes/game.tscn");
_contributor = GD.Load<PackedScene>("res://scenes/contributor.tscn");
_levelGraphEditor = GD.Load<PackedScene>("res://scenes/levelGraphEditor.tscn");
//Register ItemTypes from file
//从文件注册物品类型
ItemTypeRegister.RegisterFromFile();
//Hardcoded ItemTypes Register
//硬编码注册物品类型
ItemTypeRegister.StaticRegister();
//Register ItemTypes from file
//从文件注册物品类型
ItemTypeRegister.RegisterFromFile();
//Hardcoded ItemTypes Register
//硬编码注册物品类型
ItemTypeRegister.StaticRegister();
//静态注册掉落表
LootRegister.StaticRegister();
}
//静态注册掉落表
LootRegister.StaticRegister();
}
public override void InitializeUi()
{
_contributorButton = GetNode<LinkButton>("VBoxContainer2/ContributorButton");
_startGameButton = GetNode<Button>("StartGameButton");
_levelGraphEditorButton = GetNode<Button>("levelGraphEditorButton");
//The level map editor is only available in debug mode.
//关卡图编辑器仅在调试模式可用。
_levelGraphEditorButton.Visible = Config.IsDebug();
_startGameButton.GrabFocus();
_versionLabel = GetNode<Label>("VBoxContainer2/VersionLabel");
//Generative copyright
//生成版权
_copyrightLabel = GetNode<Label>("VBoxContainer/CopyrightLabel");
_sloganLabel = GetNode<Label>("CenterContainer2/SloganLabel");
_copyrightBuilder = new StringBuilder();
_copyrightBuilder.Append('\u00a9');
var currentYear = DateTime.Now.Year;
_copyrightBuilder.Append(Config.CreationYear);
if (currentYear != Config.CreationYear)
{
_copyrightBuilder.Append('-');
_copyrightBuilder.Append(currentYear);
}
public override void InitializeUi()
{
_contributorButton = GetNode<LinkButton>("VBoxContainer2/ContributorButton");
_startGameButton = GetNode<Button>("StartGameButton");
_levelGraphEditorButton = GetNode<Button>("levelGraphEditorButton");
//The level map editor is only available in debug mode.
//关卡图编辑器仅在调试模式可用。
_levelGraphEditorButton.Visible = Config.IsDebug();
_startGameButton.GrabFocus();
_versionLabel = GetNode<Label>("VBoxContainer2/VersionLabel");
//Generative copyright
//生成版权
_copyrightLabel = GetNode<Label>("VBoxContainer/CopyrightLabel");
_sloganLabel = GetNode<Label>("CenterContainer2/SloganLabel");
_copyrightBuilder = new StringBuilder();
_copyrightBuilder.Append('\u00a9');
var currentYear = DateTime.Now.Year;
_copyrightBuilder.Append(Config.CreationYear);
if (currentYear != Config.CreationYear)
{
_copyrightBuilder.Append('-');
_copyrightBuilder.Append(currentYear);
}
_copyrightBuilder.Append(' ');
_copyrightBuilder.Append(Config.CompanyName);
_copyrightBuilder.Append(" all rights reserved.");
_copyrightLabel.Text = _copyrightBuilder.ToString();
_versionLabel.Text = "ver." + Config.GetVersion();
_sloganLabel.Text = SloganProvider.GetSlogan();
_contributorButton.Text =
TranslationServerUtils.TranslateWithFormat("ui_contributor_tips",
ContributorDataManager.GetContributorTotals());
}
_copyrightBuilder.Append(' ');
_copyrightBuilder.Append(Config.CompanyName);
_copyrightBuilder.Append(" all rights reserved.");
_copyrightLabel.Text = _copyrightBuilder.ToString();
_versionLabel.Text = "ver." + Config.GetVersion();
_sloganLabel.Text = SloganProvider.GetSlogan();
_contributorButton.Text =
TranslationServerUtils.TranslateWithFormat("ui_contributor_tips",
ContributorDataManager.GetContributorTotals());
}
public override void LoadUiActions()
{
if (_startGameButton != null)
{
_startGameButton.Pressed += () =>
{
if (_gameScene == null)
{
return;
}
GetTree().ChangeSceneToPacked(_gameScene);
};
}
public override void LoadUiActions()
{
if (_startGameButton != null)
{
_startGameButton.Pressed += () =>
{
if (_gameScene == null)
{
return;
}
GetTree().ChangeSceneToPacked(_gameScene);
};
}
if (_contributorButton!=null)
{
_contributorButton.Pressed += () =>
{
if (_contributor == null)
{
return;
}
GetTree().ChangeSceneToPacked(_contributor);
};
}
if (_contributorButton!=null)
{
_contributorButton.Pressed += () =>
{
if (_contributor == null)
{
return;
}
GetTree().ChangeSceneToPacked(_contributor);
};
}
if (_levelGraphEditorButton != null)
{
_levelGraphEditorButton.Pressed += () =>
{
LogCat.Log("level_graph_editor");
if (_levelGraphEditor == null)
{
return;
}
GetTree().ChangeSceneToPacked(_levelGraphEditor);
};
}
}
if (_levelGraphEditorButton != null)
{
_levelGraphEditorButton.Pressed += () =>
{
LogCat.Log("level_graph_editor");
if (_levelGraphEditor == null)
{
return;
}
GetTree().ChangeSceneToPacked(_levelGraphEditor);
};
}
}
}

View File

@ -1,10 +1,8 @@
using System;
using ColdMint.scripts.camp;
using ColdMint.scripts.character;
using ColdMint.scripts.damage;
using ColdMint.scripts.item;
using Godot;
namespace ColdMint.scripts.pickable;
@ -39,6 +37,7 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
[Export] private int _maxContactInjury = 2;
public string? Description => UniqueDescription ?? ItemTypeManager.DefaultDescriptionOf(Id);
public int Quantity { get; set; }
/// <summary>
/// <para>The number of tile maps that come into contact with this item</para>
@ -58,7 +57,11 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
/// </summary>
public bool Picked { get; set; }
public virtual void Use(Node2D? owner, Vector2 targetGlobalPosition) { }
public int MaxQuantity { get; set; }
public virtual void Use(Node2D? owner, Vector2 targetGlobalPosition)
{
}
public override void _Ready()
{
@ -125,7 +128,7 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
//Determine if your side can cause damage
//判断所属的阵营是否可以造成伤害
var canCauseHarm = CampManager.CanCauseHarm(CampManager.GetCamp(ownerCharacterTemplate.CampId),
CampManager.GetCamp(characterTemplate.CampId));
CampManager.GetCamp(characterTemplate.CampId));
if (!canCauseHarm)
{
return;
@ -155,7 +158,9 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
/// <para>翻转物品</para>
/// </summary>
/// <param name="facingLeft"></param>
public void Flip(bool facingLeft) { }
public void Flip(bool facingLeft)
{
}
public virtual void Destroy()
{