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

@ -72,4 +72,7 @@ log_state_processor_not_found,找不到状态处理器{0}。,State processor {0}
log_chase_no_enemy,追逐没有敌人。,Chase no enemy.,敵がいない追跡です。
log_bubble_not_found,找不到气泡{0}。,Bubble {0} not found.,バブル{0}が見つかりません。
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
72 log_weaponContainer_is_null 武器容器为空。 Weapon container is null. 武器コンテナが空です。
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>
public Node2D? FindTheNearestItem()
{
LogCat.Log("find_nearest_item");
if (PickingRangeBodiesList == null || PickingRangeBodiesList.Count == 0)
{
return null;
}
HashSet<Node>? exclude = null;
if (_currentItem != null)
{
//Prevent picking up objects in your hands again.
//防止再次捡起自己手上的物品。
exclude = new HashSet<Node> { _currentItem };
}
return NodeUtils.GetTheNearestNode(this, PickingRangeBodiesList.ToArray(), exclude);
return NodeUtils.GetTheNearestNode(this, PickingRangeBodiesList.ToArray(), true,
node => !CanPickItem(node));
}
@ -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>
/// <para>Pick up the specified items</para>
/// <para>将指定物品拾起来</para>
/// </summary>
/// <param name="pickAbleItem"></param>
/// <param name="pickAbleItemNode2D"></param>
/// <returns>
///<para>Whether successfully picked up</para>
///<para>是否成功拾起</para>
/// </returns>
public bool PickItem(Node2D? pickAbleItem)
protected bool PickItem(Node2D? pickAbleItemNode2D)
{
//Empty reference checking is implicitly performed here.
//此处隐式的执行了空引用检查。
if (pickAbleItem is not IItem item)
if (pickAbleItemNode2D is not IItem item)
{
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;
}
@ -339,10 +361,10 @@ public partial class CharacterTemplate : CharacterBody2D
return false;
}
//First check if we can pick up the item.
//先检查我们能否拾起此物品
var canPick = ItemContainer.CanAddItem(item);
if (!canPick)
//Check to see if you can fit the item into the container first.
//先检查是否能将物品放入容器
var canAddItem = ItemContainer.CanAddItem(item);
if (!canAddItem)
{
return false;
}
@ -359,7 +381,7 @@ public partial class CharacterTemplate : CharacterBody2D
//设置捡起物品的常规处理。
//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.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.
//如果物品容器内选中的物品槽是刚刚捡到的物品,且手里没有物品持有,那么我们将选中的物品放到手上。
CurrentItem = pickAbleItem;
CurrentItem = pickAbleItemNode2D;
}
else
{
pickAbleItem.Hide();
pickAbleItem.ProcessMode = ProcessModeEnum.Disabled;
pickAbleItemNode2D.Hide();
pickAbleItemNode2D.ProcessMode = ProcessModeEnum.Disabled;
}
pickAbleItem.Reparent(ItemMarker2D);
pickAbleItem.Position = Vector2.Zero;
NodeUtils.CallDeferredReparent(ItemMarker2D, pickAbleItemNode2D);
pickAbleItemNode2D.Position = Vector2.Zero;
return true;
}

View File

@ -17,8 +17,6 @@ namespace ColdMint.scripts.character;
/// </summary>
public partial class Player : CharacterTemplate
{
private PackedScene? _floatLabelPackedScene;
private Control? _floatLabel;
//Empty object projectile
@ -52,7 +50,17 @@ public partial class Player : CharacterTemplate
CharacterName = TranslationServerUtils.Translate("default_player_name");
LogCat.LogWithFormat("player_spawn_debug", LogCat.LogLabel.Default, ReadOnlyCharacterName,
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");
_platformDetectionRayCast2D = GetNode<RayCast2D>("PlatformDetectionRayCast");
UpdateOperationTip();
@ -147,9 +155,8 @@ public partial class Player : CharacterTemplate
operationTipBuilder.Append(TranslationServerUtils.Translate("action_jump_down"));
}
//If the PickingRangeBodiesList is not null and the length is greater than 0
//如果PickingRangeBodiesList不是null且长度大于0
if (PickingRangeBodiesList is { Count: > 0 })
var nearestItem = FindTheNearestItem();
if (nearestItem != null)
{
operationTipBuilder.Append(' ');
operationTipBuilder.Append("[color=");
@ -159,6 +166,11 @@ public partial class Player : CharacterTemplate
TranslationServerUtils.Translate(InputMap.ActionGetEvents("pick_up")[0].AsText()));
operationTipBuilder.Append("[/color]");
operationTipBuilder.Append(TranslationServerUtils.Translate("action_pick_up"));
if (nearestItem is IItem item)
{
operationTipBuilder.Append(item.Name);
}
operationTipLabel.Text = operationTipBuilder.ToString();
}
@ -232,11 +244,7 @@ public partial class Player : CharacterTemplate
PickingRangeBodiesList?.Remove(pickAbleItem);
}
if (_floatLabel != null)
{
_floatLabel.QueueFree();
_floatLabel = null;
}
RecycleFloatLabel();
}
}
@ -427,35 +435,41 @@ public partial class Player : CharacterTemplate
return;
}
if (_floatLabelPackedScene != null)
if (_floatLabel != null)
{
//If there is a scene of floating text, then we generate floating text.
//如果有悬浮文本的场景,那么我们生成悬浮文本。
_floatLabel?.QueueFree();
_floatLabel = NodeUtils.InstantiatePackedScene<Control>(_floatLabelPackedScene);
if (_floatLabel != null)
if (node is not PickAbleTemplate pickAbleTemplate)
{
NodeUtils.CallDeferredAddChild(node, _floatLabel);
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();
}
return;
}
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();
@ -469,13 +483,23 @@ public partial class Player : CharacterTemplate
return;
}
if (_floatLabel != null)
RecycleFloatLabel();
UpdateOperationTip();
}
/// <summary>
/// <para>Recycle Float Label</para>
/// <para>回收悬浮标签</para>
/// </summary>
private void RecycleFloatLabel()
{
if (_floatLabel == null)
{
_floatLabel.QueueFree();
_floatLabel = null;
return;
}
UpdateOperationTip();
_floatLabel.Hide();
NodeUtils.CallDeferredReparent(this, _floatLabel);
}
protected override void OnHit(DamageTemplate damageTemplate)

View File

@ -175,17 +175,17 @@ public static class NodeUtils
///<para>Node array</para>
///<para>节点数组</para>
/// </param>
/// <param name="exclude">
///<para>Which nodes are excluded</para>
///<para>排除哪些节点</para>
/// </param>
/// <param name="excludeInvisibleNodes">
///<para>Whether or not unseen nodes should be excluded</para>
///<para>是否排除不可见的节点</para>
/// </param>
/// <param name="filter">
///<para>Filter, which returns true within the function to filter the specified node.</para>
///<para>过滤器在函数内返回true则过滤指定节点。</para>
/// </param>
/// <returns></returns>
public static Node2D? GetTheNearestNode(Node2D origin, Node[] array, HashSet<Node>? exclude = null,
bool excludeInvisibleNodes = true)
public static Node2D? GetTheNearestNode(Node2D origin, Node[] array,
bool excludeInvisibleNodes = true, Func<Node2D, bool>? filter = null)
{
var closestDistance = float.MaxValue;
Node2D? closestNode = null;
@ -199,10 +199,10 @@ public static class NodeUtils
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;
}