Fixed an issue where players could pick up items that had already been picked up by other creatures. Reuse floating label instances.

修复玩家能够捡起已被其他生物捡起的物品的问题。复用悬浮标签实例。
This commit is contained in:
Cold-Mint 2024-07-11 23:06:47 +08:00
parent 61618c13a9
commit 7365434f2b
Signed by: Cold-Mint
GPG Key ID: C5A9BF8A98E0CE99
4 changed files with 123 additions and 74 deletions

View File

@ -73,3 +73,6 @@ log_chase_no_enemy,追逐没有敌人。,Chase no enemy.,敵がいない追跡
log_bubble_not_found,找不到气泡{0}。,Bubble {0} not found.,バブル{0}が見つかりません。 log_bubble_not_found,找不到气泡{0}。,Bubble {0} not found.,バブル{0}が見つかりません。
log_owner_is_not_AiCharacter,所有者不是AiCharacter。,Owner is not AiCharacter.,所有者はAiCharacterではありません。 log_owner_is_not_AiCharacter,所有者不是AiCharacter。,Owner is not AiCharacter.,所有者はAiCharacterではありません。
log_weaponContainer_is_null,武器容器为空。,Weapon container is null.,武器コンテナが空です。 log_weaponContainer_is_null,武器容器为空。,Weapon container is null.,武器コンテナが空です。
log_find_nearest_item,查找最近的物品。,Find the nearest item.,最も近いアイテムを見つけます。
log_float_label_instantiate_failed,浮动标签实例化失败。,Float label instantiation failed.,フロートラベルのインスタンス化に失敗しました。
log_pickable_picked_up,可拾捡物被捡起了,那么不显示标签。,"If the pickable item is picked up, the label is not displayed.",でも、拾得物が拾い上げられたら、ラベルは表示されません。
1 id zh en ja
73 log_find_nearest_item 查找最近的物品。 Find the nearest item. 最も近いアイテムを見つけます。
74 log_float_label_instantiate_failed 浮动标签实例化失败。 Float label instantiation failed. フロートラベルのインスタンス化に失敗しました。
75 log_pickable_picked_up 可拾捡物被捡起了,那么不显示标签。 If the pickable item is picked up, the label is not displayed. でも、拾得物が拾い上げられたら、ラベルは表示されません。
76
77
78

View File

@ -218,20 +218,14 @@ public partial class CharacterTemplate : CharacterBody2D
/// <returns></returns> /// <returns></returns>
public Node2D? FindTheNearestItem() public Node2D? FindTheNearestItem()
{ {
LogCat.Log("find_nearest_item");
if (PickingRangeBodiesList == null || PickingRangeBodiesList.Count == 0) if (PickingRangeBodiesList == null || PickingRangeBodiesList.Count == 0)
{ {
return null; return null;
} }
HashSet<Node>? exclude = null; return NodeUtils.GetTheNearestNode(this, PickingRangeBodiesList.ToArray(), true,
if (_currentItem != null) node => !CanPickItem(node));
{
//Prevent picking up objects in your hands again.
//防止再次捡起自己手上的物品。
exclude = new HashSet<Node> { _currentItem };
}
return NodeUtils.GetTheNearestNode(this, PickingRangeBodiesList.ToArray(), exclude);
} }
@ -308,25 +302,53 @@ public partial class CharacterTemplate : CharacterBody2D
} }
} }
/// <summary>
/// <para>Whether you can pick up specified items</para>
/// <para>是否能捡起指定物品</para>
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
private bool CanPickItem(Node node)
{
if (_currentItem != null && node == _currentItem)
{
//Do not include your own belongings.
//不包含自己手上的物品。
return false;
}
if (node is PickAbleTemplate pickAbleTemplate)
{
//Does not contain items that have been picked up.
//不包含已被捡起的物品。
return !pickAbleTemplate.Picked;
}
return false;
}
/// <summary> /// <summary>
/// <para>Pick up the specified items</para> /// <para>Pick up the specified items</para>
/// <para>将指定物品拾起来</para> /// <para>将指定物品拾起来</para>
/// </summary> /// </summary>
/// <param name="pickAbleItem"></param> /// <param name="pickAbleItemNode2D"></param>
/// <returns> /// <returns>
///<para>Whether successfully picked up</para> ///<para>Whether successfully picked up</para>
///<para>是否成功拾起</para> ///<para>是否成功拾起</para>
/// </returns> /// </returns>
public bool PickItem(Node2D? pickAbleItem) protected bool PickItem(Node2D? pickAbleItemNode2D)
{ {
//Empty reference checking is implicitly performed here. //Empty reference checking is implicitly performed here.
//此处隐式的执行了空引用检查。 //此处隐式的执行了空引用检查。
if (pickAbleItem is not IItem item) if (pickAbleItemNode2D is not IItem item)
{ {
return false; return false;
} }
if (ItemContainer == null)
//The item store is marked null, or the item container is null.
//物品存放的标记为null或者物品容器为null。
if (ItemMarker2D == null || ItemContainer == null)
{ {
return false; return false;
} }
@ -339,10 +361,10 @@ public partial class CharacterTemplate : CharacterBody2D
return false; return false;
} }
//First check if we can pick up the item. //Check to see if you can fit the item into the container first.
//先检查我们能否拾起此物品 //先检查是否能将物品放入容器
var canPick = ItemContainer.CanAddItem(item); var canAddItem = ItemContainer.CanAddItem(item);
if (!canPick) if (!canAddItem)
{ {
return false; return false;
} }
@ -359,7 +381,7 @@ public partial class CharacterTemplate : CharacterBody2D
//设置捡起物品的常规处理。 //设置捡起物品的常规处理。
//You can supplement picking up state handling for more types of objects here. //You can supplement picking up state handling for more types of objects here.
//您可以在这里补充更多类型对象的捡起状态处理。 //您可以在这里补充更多类型对象的捡起状态处理。
if (pickAbleItem is PickAbleTemplate pickAbleTemplate) if (pickAbleItemNode2D is PickAbleTemplate pickAbleTemplate)
{ {
pickAbleTemplate.Owner = this; pickAbleTemplate.Owner = this;
pickAbleTemplate.Picked = true; pickAbleTemplate.Picked = true;
@ -374,16 +396,16 @@ public partial class CharacterTemplate : CharacterBody2D
{ {
//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.
//如果物品容器内选中的物品槽是刚刚捡到的物品,且手里没有物品持有,那么我们将选中的物品放到手上。 //如果物品容器内选中的物品槽是刚刚捡到的物品,且手里没有物品持有,那么我们将选中的物品放到手上。
CurrentItem = pickAbleItem; CurrentItem = pickAbleItemNode2D;
} }
else else
{ {
pickAbleItem.Hide(); pickAbleItemNode2D.Hide();
pickAbleItem.ProcessMode = ProcessModeEnum.Disabled; pickAbleItemNode2D.ProcessMode = ProcessModeEnum.Disabled;
} }
pickAbleItem.Reparent(ItemMarker2D); NodeUtils.CallDeferredReparent(ItemMarker2D, pickAbleItemNode2D);
pickAbleItem.Position = Vector2.Zero; pickAbleItemNode2D.Position = Vector2.Zero;
return true; return true;
} }

View File

@ -17,8 +17,6 @@ namespace ColdMint.scripts.character;
/// </summary> /// </summary>
public partial class Player : CharacterTemplate public partial class Player : CharacterTemplate
{ {
private PackedScene? _floatLabelPackedScene;
private Control? _floatLabel; private Control? _floatLabel;
//Empty object projectile //Empty object projectile
@ -52,7 +50,17 @@ public partial class Player : CharacterTemplate
CharacterName = TranslationServerUtils.Translate("default_player_name"); CharacterName = TranslationServerUtils.Translate("default_player_name");
LogCat.LogWithFormat("player_spawn_debug", LogCat.LogLabel.Default, ReadOnlyCharacterName, LogCat.LogWithFormat("player_spawn_debug", LogCat.LogLabel.Default, ReadOnlyCharacterName,
GlobalPosition); GlobalPosition);
_floatLabelPackedScene = GD.Load<PackedScene>("res://prefab/ui/FloatLabel.tscn"); var floatLabelPackedScene = GD.Load<PackedScene>("res://prefab/ui/FloatLabel.tscn");
//Initializes the float label.
//初始化悬浮标签。
_floatLabel = NodeUtils.InstantiatePackedScene<Control>(floatLabelPackedScene);
if (_floatLabel == null)
{
throw new NullReferenceException(TranslationServer.Translate("float_label_instantiate_failed"));
}
_floatLabel.Hide();
NodeUtils.CallDeferredAddChild(this, _floatLabel);
_parabola = GetNode<Line2D>("Parabola"); _parabola = GetNode<Line2D>("Parabola");
_platformDetectionRayCast2D = GetNode<RayCast2D>("PlatformDetectionRayCast"); _platformDetectionRayCast2D = GetNode<RayCast2D>("PlatformDetectionRayCast");
UpdateOperationTip(); UpdateOperationTip();
@ -147,9 +155,8 @@ public partial class Player : CharacterTemplate
operationTipBuilder.Append(TranslationServerUtils.Translate("action_jump_down")); operationTipBuilder.Append(TranslationServerUtils.Translate("action_jump_down"));
} }
//If the PickingRangeBodiesList is not null and the length is greater than 0 var nearestItem = FindTheNearestItem();
//如果PickingRangeBodiesList不是null且长度大于0 if (nearestItem != null)
if (PickingRangeBodiesList is { Count: > 0 })
{ {
operationTipBuilder.Append(' '); operationTipBuilder.Append(' ');
operationTipBuilder.Append("[color="); operationTipBuilder.Append("[color=");
@ -159,6 +166,11 @@ public partial class Player : CharacterTemplate
TranslationServerUtils.Translate(InputMap.ActionGetEvents("pick_up")[0].AsText())); TranslationServerUtils.Translate(InputMap.ActionGetEvents("pick_up")[0].AsText()));
operationTipBuilder.Append("[/color]"); operationTipBuilder.Append("[/color]");
operationTipBuilder.Append(TranslationServerUtils.Translate("action_pick_up")); operationTipBuilder.Append(TranslationServerUtils.Translate("action_pick_up"));
if (nearestItem is IItem item)
{
operationTipBuilder.Append(item.Name);
}
operationTipLabel.Text = operationTipBuilder.ToString(); operationTipLabel.Text = operationTipBuilder.ToString();
} }
@ -232,11 +244,7 @@ public partial class Player : CharacterTemplate
PickingRangeBodiesList?.Remove(pickAbleItem); PickingRangeBodiesList?.Remove(pickAbleItem);
} }
if (_floatLabel != null) RecycleFloatLabel();
{
_floatLabel.QueueFree();
_floatLabel = null;
}
} }
} }
@ -427,35 +435,41 @@ public partial class Player : CharacterTemplate
return; return;
} }
if (_floatLabelPackedScene != null) if (_floatLabel != null)
{ {
//If there is a scene of floating text, then we generate floating text. if (node is not PickAbleTemplate pickAbleTemplate)
//如果有悬浮文本的场景,那么我们生成悬浮文本。
_floatLabel?.QueueFree();
_floatLabel = NodeUtils.InstantiatePackedScene<Control>(_floatLabelPackedScene);
if (_floatLabel != null)
{ {
NodeUtils.CallDeferredAddChild(node, _floatLabel); return;
var rotationDegreesNode2D = node2D.RotationDegrees;
var rotationDegreesNode2DAbs = Math.Abs(rotationDegreesNode2D);
_floatLabel.Position = rotationDegreesNode2DAbs > 90
? new Vector2(0, PromptTextDistance)
: new Vector2(0, -PromptTextDistance);
_floatLabel.RotationDegrees = 0 - rotationDegreesNode2D;
var label = _floatLabel.GetNode<Label>("Label");
if (node is PickAbleTemplate pickAbleTemplate)
{
var stringBuilder = new StringBuilder();
if (pickAbleTemplate.Owner is CharacterTemplate characterTemplate)
{
stringBuilder.Append(characterTemplate.ReadOnlyCharacterName);
stringBuilder.Append(TranslationServerUtils.Translate("de"));
}
stringBuilder.Append(TranslationServerUtils.Translate(pickAbleTemplate.Name));
label.Text = stringBuilder.ToString();
}
} }
if (pickAbleTemplate.Picked)
{
//If the pickables are picked up, the label is not displayed.
//如果可拾捡物被捡起了,那么不显示标签。
LogCat.LogWarning("pickable_picked_up");
return;
}
NodeUtils.CallDeferredReparent(node, _floatLabel);
var rotationDegreesNode2D = node2D.RotationDegrees;
var rotationDegreesNode2DAbs = Math.Abs(rotationDegreesNode2D);
_floatLabel.GlobalPosition = node2D.GlobalPosition;
_floatLabel.Position = rotationDegreesNode2DAbs > 90
? new Vector2(0, PromptTextDistance)
: new Vector2(0, -PromptTextDistance);
_floatLabel.RotationDegrees = 0 - rotationDegreesNode2D;
var label = _floatLabel.GetNode<Label>("Label");
var stringBuilder = new StringBuilder();
if (pickAbleTemplate.Owner is CharacterTemplate characterTemplate)
{
stringBuilder.Append(characterTemplate.ReadOnlyCharacterName);
stringBuilder.Append(TranslationServerUtils.Translate("de"));
}
stringBuilder.Append(TranslationServerUtils.Translate(pickAbleTemplate.Name));
label.Text = stringBuilder.ToString();
_floatLabel.Show();
} }
UpdateOperationTip(); UpdateOperationTip();
@ -469,13 +483,23 @@ public partial class Player : CharacterTemplate
return; return;
} }
if (_floatLabel != null) RecycleFloatLabel();
UpdateOperationTip();
}
/// <summary>
/// <para>Recycle Float Label</para>
/// <para>回收悬浮标签</para>
/// </summary>
private void RecycleFloatLabel()
{
if (_floatLabel == null)
{ {
_floatLabel.QueueFree(); return;
_floatLabel = null;
} }
UpdateOperationTip(); _floatLabel.Hide();
NodeUtils.CallDeferredReparent(this, _floatLabel);
} }
protected override void OnHit(DamageTemplate damageTemplate) protected override void OnHit(DamageTemplate damageTemplate)

View File

@ -175,17 +175,17 @@ public static class NodeUtils
///<para>Node array</para> ///<para>Node array</para>
///<para>节点数组</para> ///<para>节点数组</para>
/// </param> /// </param>
/// <param name="exclude">
///<para>Which nodes are excluded</para>
///<para>排除哪些节点</para>
/// </param>
/// <param name="excludeInvisibleNodes"> /// <param name="excludeInvisibleNodes">
///<para>Whether or not unseen nodes should be excluded</para> ///<para>Whether or not unseen nodes should be excluded</para>
///<para>是否排除不可见的节点</para> ///<para>是否排除不可见的节点</para>
/// </param> /// </param>
/// <param name="filter">
///<para>Filter, which returns true within the function to filter the specified node.</para>
///<para>过滤器在函数内返回true则过滤指定节点。</para>
/// </param>
/// <returns></returns> /// <returns></returns>
public static Node2D? GetTheNearestNode(Node2D origin, Node[] array, HashSet<Node>? exclude = null, public static Node2D? GetTheNearestNode(Node2D origin, Node[] array,
bool excludeInvisibleNodes = true) bool excludeInvisibleNodes = true, Func<Node2D, bool>? filter = null)
{ {
var closestDistance = float.MaxValue; var closestDistance = float.MaxValue;
Node2D? closestNode = null; Node2D? closestNode = null;
@ -199,10 +199,10 @@ public static class NodeUtils
continue; continue;
} }
if (exclude != null && exclude.Contains(node)) if (filter != null && filter.Invoke(node2D))
{ {
//If the current node, is within our exclusion project. So the next one. //If there is a filter, and the filter returns true, then the next.
//如果当前节点,在我们的排除项目内。那么下一个。 //如果有过滤器且过滤器返回true那么下一个。
continue; continue;
} }