Optimized drag and drop of items.

优化物品的拖拽。
This commit is contained in:
Cold-Mint 2024-06-18 23:37:18 +08:00
parent c1f0e30671
commit a58ddeb039
Signed by: Cold-Mint
GPG Key ID: C5A9BF8A98E0CE99
5 changed files with 207 additions and 57 deletions

View File

@ -2,7 +2,7 @@
importer="csv_translation" importer="csv_translation"
type="Translation" type="Translation"
uid="uid://bgfmprv2sm645" uid="uid://cc0k86apkvut7"
[deps] [deps]

View File

@ -1,18 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using ColdMint.scripts.camp; using ColdMint.scripts.camp;
using ColdMint.scripts.damage; using ColdMint.scripts.damage;
using ColdMint.scripts.debug; using ColdMint.scripts.debug;
using ColdMint.scripts.health; using ColdMint.scripts.health;
using ColdMint.scripts.inventory; using ColdMint.scripts.inventory;
using ColdMint.scripts.item; using ColdMint.scripts.item;
using ColdMint.scripts.item.itemStacks;
using ColdMint.scripts.utils; using ColdMint.scripts.utils;
using ColdMint.scripts.item.weapon; using ColdMint.scripts.item.weapon;
using ColdMint.scripts.loot; using ColdMint.scripts.loot;
using ColdMint.scripts.pickable; using ColdMint.scripts.pickable;
using Godot; using Godot;
namespace ColdMint.scripts.character; namespace ColdMint.scripts.character;
@ -67,7 +66,9 @@ public partial class CharacterTemplate : CharacterBody2D
///<para>Update finished items</para> ///<para>Update finished items</para>
///<para>更新完成后的物品</para> ///<para>更新完成后的物品</para>
/// </param> /// </param>
protected virtual void WhenUpdateCurrentItem(Node2D? currentItem) { } protected virtual void WhenUpdateCurrentItem(Node2D? currentItem)
{
}
//Define a pickup range //Define a pickup range
//定义一个拾起范围 //定义一个拾起范围
@ -203,8 +204,8 @@ public partial class CharacterTemplate : CharacterBody2D
base._Ready(); base._Ready();
PickingRangeBodiesList = new List<Node>(); PickingRangeBodiesList = new List<Node>();
CharacterName = GetMeta("Name", Name).AsString(); CharacterName = GetMeta("Name", Name).AsString();
CampId = GetMeta("CampId", Config.CampId.Default).AsString(); CampId = GetMeta("CampId", Config.CampId.Default).AsString();
MaxHp = GetMeta("MaxHp", Config.DefaultMaxHp).AsInt32(); MaxHp = GetMeta("MaxHp", Config.DefaultMaxHp).AsInt32();
// var lootListId = GetMeta("LootListId", string.Empty).AsString(); // var lootListId = GetMeta("LootListId", string.Empty).AsString();
if (MaxHp <= 0) if (MaxHp <= 0)
@ -275,8 +276,8 @@ public partial class CharacterTemplate : CharacterBody2D
//Get the currently selected node //Get the currently selected node
//拿到当前选择的节点 //拿到当前选择的节点
var itemSlotNode = ItemContainer.GetSelectItemSlotNode(); var selectItemSlotNode = ItemContainer.GetSelectItemSlotNode();
if (itemSlotNode == null) if (selectItemSlotNode == null)
{ {
return false; return false;
} }
@ -306,12 +307,14 @@ public partial class CharacterTemplate : CharacterBody2D
pickAbleTemplate.Owner = this; pickAbleTemplate.Owner = this;
pickAbleTemplate.Picked = true; pickAbleTemplate.Picked = true;
pickAbleTemplate.SetCollisionMaskValue(Config.LayerNumber.Platform, false); pickAbleTemplate.SetCollisionMaskValue(Config.LayerNumber.Platform, false);
pickAbleTemplate.SetCollisionMaskValue(Config.LayerNumber.Ground, false); pickAbleTemplate.SetCollisionMaskValue(Config.LayerNumber.Ground, false);
pickAbleTemplate.EnableContactInjury = false; pickAbleTemplate.EnableContactInjury = false;
pickAbleTemplate.Sleeping = true; pickAbleTemplate.Sleeping = true;
} }
if (itemSlotNode.GetItem() != null && itemSlotNode.GetItem() == item && _currentItem == null) var itemStack = selectItemSlotNode.GetItemStack();
var itemFromStack = itemStack?.GetItem();
if (itemFromStack != null && itemFromStack == item && _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. //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.
//如果物品容器内选中的物品槽是刚刚捡到的物品,且手里没有物品持有,那么我们将选中的物品放到手上。 //如果物品容器内选中的物品槽是刚刚捡到的物品,且手里没有物品持有,那么我们将选中的物品放到手上。
@ -466,8 +469,8 @@ public partial class CharacterTemplate : CharacterBody2D
/// <param name="lootData"></param> /// <param name="lootData"></param>
/// <param name="position"></param> /// <param name="position"></param>
public void GenerateLootObjects(Node parentNode, public void GenerateLootObjects(Node parentNode,
IEnumerable<LootDatum> lootData, IEnumerable<LootDatum> lootData,
Vector2 position) Vector2 position)
{ {
foreach (var lootDatum in lootData) foreach (var lootDatum in lootData)
{ {
@ -486,7 +489,9 @@ public partial class CharacterTemplate : CharacterBody2D
_additionalForce = force; _additionalForce = force;
} }
protected virtual void OnHit(DamageTemplate damageTemplate) { } protected virtual void OnHit(DamageTemplate damageTemplate)
{
}
/// <summary> /// <summary>
/// <para>Handle the event of character death</para> /// <para>Handle the event of character death</para>
@ -669,7 +674,7 @@ public partial class CharacterTemplate : CharacterBody2D
//We cannot immediately resume the physical collision when the weapon is discharged, which will cause the weapon to collide with the ground and platform earlier, preventing the weapon from flying. //We cannot immediately resume the physical collision when the weapon is discharged, which will cause the weapon to collide with the ground and platform earlier, preventing the weapon from flying.
//仍出武器时,我们不能立即恢复物理碰撞,立即恢复会导致武器更早的与地面和平台碰撞,阻止武器的飞行。 //仍出武器时,我们不能立即恢复物理碰撞,立即恢复会导致武器更早的与地面和平台碰撞,阻止武器的飞行。
pickAbleTemplate.EnableContactInjury = true; pickAbleTemplate.EnableContactInjury = true;
pickAbleTemplate.SetCollisionMaskValue(Config.LayerNumber.Ground, true); pickAbleTemplate.SetCollisionMaskValue(Config.LayerNumber.Ground, true);
pickAbleTemplate.SetCollisionMaskValue(Config.LayerNumber.Platform, true); pickAbleTemplate.SetCollisionMaskValue(Config.LayerNumber.Platform, true);
timer.QueueFree(); timer.QueueFree();
}; };

View File

@ -1,6 +1,7 @@
using ColdMint.scripts.debug; using System;
using ColdMint.scripts.item; using ColdMint.scripts.item;
using ColdMint.scripts.item.itemStacks; using ColdMint.scripts.item.itemStacks;
using ColdMint.scripts.map.events;
using ColdMint.scripts.utils; using ColdMint.scripts.utils;
using Godot; using Godot;
@ -20,12 +21,27 @@ public partial class ItemSlotNode : MarginContainer
private bool _isSelect; private bool _isSelect;
private Texture2D? _backgroundTexture; private Texture2D? _backgroundTexture;
private Texture2D? _backgroundTextureWhenSelect; private Texture2D? _backgroundTextureWhenSelect;
public Action<ItemStackChangeEvent>? ItemStackChangeEvent;
public override void _Ready()
{
_backgroundTexture = GD.Load<Texture2D>("res://sprites/ui/ItemBarEmpty.png");
_backgroundTextureWhenSelect = GD.Load<Texture2D>("res://sprites/ui/ItemBarFocus.png");
_backgroundTextureRect =
GetNode<TextureRect>("BackgroundTexture");
_iconTextureRect = GetNode<TextureRect>("BackgroundTexture/IconTextureRect");
_quantityLabel = GetNode<Label>("Control/QuantityLabel");
_control = GetNode<Control>("Control");
_quantityLabel.Hide();
}
public override Variant _GetDragData(Vector2 atPosition) public override Variant _GetDragData(Vector2 atPosition)
{ {
if (_iconTextureRect == null) if (_iconTextureRect == null || _itemStack == null)
{ {
return base._GetDragData(atPosition); //Drag is not allowed if there is no icon or no pile of items.
//如果没有图标或者没有物品堆,那么不允许拖动。
return new Variant();
} }
var textureRect = new TextureRect(); var textureRect = new TextureRect();
@ -33,20 +49,63 @@ public partial class ItemSlotNode : MarginContainer
textureRect.Size = _iconTextureRect.Size; textureRect.Size = _iconTextureRect.Size;
textureRect.Texture = _iconTextureRect.Texture; textureRect.Texture = _iconTextureRect.Texture;
SetDragPreview(textureRect); SetDragPreview(textureRect);
return Variant.From(this); return Variant.CreateFrom(this);
} }
public override bool _CanDropData(Vector2 atPosition, Variant data) public override bool _CanDropData(Vector2 atPosition, Variant data)
{ {
//If the preplaced slot does not have an icon, the preplaced slot is not allowed.
//如果预放置的槽位没有图标,那么不允许放置。
if (_iconTextureRect == null) if (_iconTextureRect == null)
{ {
return false; return false;
} }
//TODO:在这里判断是否可以放置物品。物品槽必须是空的。 var type = data.VariantType;
// var itemSlotNode = data.As<ItemSlotNode>(); if (type == Variant.Type.Nil)
// itemSlotNode._itemStack {
return true; //The preplaced data is null.
//预放置的数据为null。
return false;
}
var itemSlotNode = data.As<ItemSlotNode>();
var itemStack = itemSlotNode.GetItemStack();
if (itemStack == null)
{
//Return null when trying to get the source item heap.
//尝试获取源物品堆时返回null。
return false;
}
//TODOThis is where we infer whether the two piles can merge.在这里推断两个物品堆是否可以融合。
return _itemStack == null;
}
public override void _DropData(Vector2 atPosition, Variant data)
{
if (_iconTextureRect == null)
{
return;
}
var type = data.VariantType;
if (type == Variant.Type.Nil)
{
//The passed variable is null.
//传入的变量为null。
return;
}
var itemSlotNode = data.As<ItemSlotNode>();
var itemStack = itemSlotNode.ReplaceItemStack(null);
if (itemStack == null)
{
//Return null when trying to get the source item heap.
//尝试获取源物品堆时返回null。
return;
}
ReplaceItemStack(itemStack);
} }
public bool IsSelect public bool IsSelect
@ -74,12 +133,6 @@ public partial class ItemSlotNode : MarginContainer
/// <returns></returns> /// <returns></returns>
public IItemStack? GetItemStack() => _itemStack; public IItemStack? GetItemStack() => _itemStack;
/// <summary>
/// <para>If present, get the item at the top of the item stack in this slot</para>
/// <para>如果存在,获取该槽位中物品堆顶部的物品</para>
/// </summary>
public IItem? GetItem() => _itemStack?.GetItem();
/// <summary> /// <summary>
/// <para>If present, remove an item in this slot and return it.</para> /// <para>If present, remove an item in this slot and return it.</para>
/// <para>如果存在,移除该槽位中的一个物品并将其返回</para> /// <para>如果存在,移除该槽位中的一个物品并将其返回</para>
@ -90,7 +143,11 @@ public partial class ItemSlotNode : MarginContainer
if (_itemStack is null) return null; if (_itemStack is null) return null;
var result = _itemStack.PickItem(); var result = _itemStack.PickItem();
if (_itemStack.Empty) _itemStack = null; if (_itemStack.Empty)
{
SetItemStack(null);
}
UpdateAllDisplay(); UpdateAllDisplay();
return result; return result;
@ -110,7 +167,11 @@ public partial class ItemSlotNode : MarginContainer
if (_itemStack is null) return null; if (_itemStack is null) return null;
var result = _itemStack.PickItems(value); var result = _itemStack.PickItems(value);
if (_itemStack.Empty) _itemStack = null; if (_itemStack.Empty)
{
SetItemStack(null);
}
UpdateAllDisplay(); UpdateAllDisplay();
return result; return result;
@ -142,7 +203,11 @@ public partial class ItemSlotNode : MarginContainer
var result = _itemStack.RemoveItem(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. //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。那么我们清空物品栏。 //如果移除指定数量的物品后物品数量小于或等于0。那么我们清空物品栏。
if (_itemStack.Empty) _itemStack = null; if (_itemStack.Empty)
{
SetItemStack(null);
}
UpdateAllDisplay(); UpdateAllDisplay();
return result; return result;
@ -166,8 +231,7 @@ public partial class ItemSlotNode : MarginContainer
public void ClearSlot() public void ClearSlot()
{ {
_itemStack?.ClearStack(); _itemStack?.ClearStack();
_itemStack = null; SetItemStack(null);
UpdateAllDisplay(); UpdateAllDisplay();
} }
@ -185,8 +249,7 @@ public partial class ItemSlotNode : MarginContainer
public IItemStack? ReplaceItemStack(IItemStack? newItemStack) public IItemStack? ReplaceItemStack(IItemStack? newItemStack)
{ {
var result = _itemStack; var result = _itemStack;
_itemStack = newItemStack; SetItemStack(newItemStack);
UpdateAllDisplay(); UpdateAllDisplay();
return result; return result;
@ -213,7 +276,7 @@ public partial class ItemSlotNode : MarginContainer
bool result; bool result;
if (_itemStack is null) if (_itemStack is null)
{ {
_itemStack = IItemStack.FromItem(item); SetItemStack(IItemStack.FromItem(item));
result = true; result = true;
} }
else else
@ -257,7 +320,7 @@ public partial class ItemSlotNode : MarginContainer
bool result; bool result;
if (_itemStack is null) if (_itemStack is null)
{ {
_itemStack = itemStack; SetItemStack(itemStack);
result = false; result = false;
} }
else else
@ -287,10 +350,9 @@ public partial class ItemSlotNode : MarginContainer
/// </summary> /// </summary>
private void UpdateTooltipText() private void UpdateTooltipText()
{ {
if (_control == null) return;
if (_itemStack == null) if (_itemStack == null)
{ {
_control.TooltipText = null; TooltipText = null;
return; return;
} }
@ -299,7 +361,7 @@ public partial class ItemSlotNode : MarginContainer
var debugText = TranslationServerUtils.Translate("item_prompt_debug"); var debugText = TranslationServerUtils.Translate("item_prompt_debug");
if (debugText != null) if (debugText != null)
{ {
_control.TooltipText = string.Format(debugText, _itemStack.GetItem()?.Id, TooltipText = string.Format(debugText, _itemStack.GetItem()?.Id,
TranslationServerUtils.Translate(_itemStack.Name), TranslationServerUtils.Translate(_itemStack.Name),
_itemStack.Quantity, _itemStack.MaxQuantity, _itemStack.GetType().Name, _itemStack.Quantity, _itemStack.MaxQuantity, _itemStack.GetType().Name,
TranslationServerUtils.Translate(_itemStack.Description)); TranslationServerUtils.Translate(_itemStack.Description));
@ -307,8 +369,8 @@ public partial class ItemSlotNode : MarginContainer
} }
else else
{ {
_control.TooltipText = TranslationServerUtils.Translate(_itemStack.Name) + "\n" + TooltipText = TranslationServerUtils.Translate(_itemStack.Name) + "\n" +
TranslationServerUtils.Translate(_itemStack.Description); TranslationServerUtils.Translate(_itemStack.Description);
} }
} }
@ -337,6 +399,25 @@ public partial class ItemSlotNode : MarginContainer
} }
} }
/// <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;
var stackChangeEvent = new ItemStackChangeEvent
{
ItemStack = itemStack
};
ItemStackChangeEvent?.Invoke(stackChangeEvent);
}
/// <summary> /// <summary>
/// <para>Update texture of the icon rect</para> /// <para>Update texture of the icon rect</para>
/// <para>更新显示的物品图标</para> /// <para>更新显示的物品图标</para>
@ -349,15 +430,5 @@ public partial class ItemSlotNode : MarginContainer
} }
} }
public override void _Ready()
{
_backgroundTexture = GD.Load<Texture2D>("res://sprites/ui/ItemBarEmpty.png");
_backgroundTextureWhenSelect = GD.Load<Texture2D>("res://sprites/ui/ItemBarFocus.png");
_backgroundTextureRect =
GetNode<TextureRect>("BackgroundTexture");
_iconTextureRect = GetNode<TextureRect>("BackgroundTexture/IconTextureRect");
_quantityLabel = GetNode<Label>("Control/QuantityLabel");
_control = GetNode<Control>("Control");
_quantityLabel.Hide();
}
} }

View File

@ -234,6 +234,32 @@ public class UniversalItemContainer : IItemContainer
itemSlotNode.IsSelect = (_itemSlotNodes.Count) == _selectIndex; itemSlotNode.IsSelect = (_itemSlotNodes.Count) == _selectIndex;
_itemSlotNodes.Add(itemSlotNode); _itemSlotNodes.Add(itemSlotNode);
// itemSlotNode.ItemStackChangeEvent += @event =>
// {
// if (_itemSlotNodes == null)
// {
// return;
// }
//
// var index = _itemSlotNodes.IndexOf(itemSlotNode);
// // LogCat.Log("位于" + index + "的堆改变了。" + _selectIndex + "空的吗" + (@event.ItemStack == null));
// if (index == -1)
// {
// return;
// }
//
// if (index == _selectIndex)
// {
// if (@event.ItemStack == null)
// {
// HideItem(index);
// }
// else
// {
// DisplayItem(index);
// }
// }
// };
return itemSlotNode; return itemSlotNode;
} }
@ -317,14 +343,48 @@ public class UniversalItemContainer : IItemContainer
_itemSlotNodes[oldSelectIndex].IsSelect = false; _itemSlotNodes[oldSelectIndex].IsSelect = false;
_itemSlotNodes[newSelectIndex].IsSelect = true; _itemSlotNodes[newSelectIndex].IsSelect = true;
var oldItem = _itemSlotNodes[oldSelectIndex].GetItem(); HideItem(oldSelectIndex);
DisplayItem(newSelectIndex);
_selectIndex = newSelectIndex;
}
/// <summary>
/// <para>HideItem</para>
/// <para>隐藏某个物品</para>
/// </summary>
/// <param name="index"></param>
private void HideItem(int index)
{
if (_itemSlotNodes == null)
{
return;
}
var oldItem = _itemSlotNodes[index].GetItemStack()?.GetItem();
if (oldItem is Node2D oldNode2D) if (oldItem is Node2D oldNode2D)
{ {
oldNode2D.ProcessMode = Node.ProcessModeEnum.Disabled; oldNode2D.ProcessMode = Node.ProcessModeEnum.Disabled;
oldNode2D.Hide(); oldNode2D.Hide();
} }
}
var item = _itemSlotNodes[newSelectIndex].GetItem(); /// <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)
{
if (_itemSlotNodes == null)
{
return;
}
var item = _itemSlotNodes[index].GetItemStack()?.GetItem();
switch (item) switch (item)
{ {
case null: case null:
@ -357,8 +417,6 @@ public class UniversalItemContainer : IItemContainer
break; break;
} }
} }
_selectIndex = newSelectIndex;
} }

View File

@ -0,0 +1,16 @@
using ColdMint.scripts.item.itemStacks;
namespace ColdMint.scripts.map.events;
/// <summary>
/// <para>ItemStackChangeEvent</para>
/// <para>物品堆改变事件</para>
/// </summary>
public class ItemStackChangeEvent
{
/// <summary>
/// <para>Changed ItemStack</para>
/// <para>改变后的物品堆</para>
/// </summary>
public IItemStack? ItemStack { get; set; }
}