using System;
using System.Threading.Tasks;
using ColdMint.scripts.debug;
using ColdMint.scripts.projectile;
using Godot;
using Packsack = ColdMint.scripts.inventory.Packsack;
using PacksackUi = ColdMint.scripts.loader.uiLoader.PacksackUi;
using WeaponTemplate = ColdMint.scripts.weapon.WeaponTemplate;
namespace ColdMint.scripts.utils;
///
/// Node Utils
/// 节点工具
///
public static class NodeUtils
{
///
/// Delete all child nodes
/// 删除所有子节点
///
///
public static int DeleteAllChild(Node parent)
{
var deleteNumber = 0;
var count = parent.GetChildCount();
if (count <= 0) return deleteNumber;
for (var i = 0; i < count; i++)
{
var node = parent.GetChild(0);
parent.RemoveChild(node);
node.QueueFree();
deleteNumber++;
}
return deleteNumber;
}
///
/// Call the method to set the parent node at leisure
/// 在空闲时刻调用设置父节点的方法
///
///
///
public static void CallDeferredReparent(Node parentNode, Node childNode)
{
childNode.CallDeferred("reparent", parentNode);
}
///
/// ShowNode
/// 显示节点
///
///
///node
///节点
///
///
///Is it displayed successfully?
///是否显示成功
///
public static bool ShowNode(Node node)
{
if (node is Node2D node2D)
{
node2D.Show();
return true;
}
if (node is CanvasItem canvasItem)
{
canvasItem.Show();
return true;
}
return false;
}
///
/// hidden node
/// 隐藏节点
///
///
///Node to hide
///要隐藏的节点
///
///
///Hide success or not
///是否隐藏成功
///
public static bool HideNode(Node node)
{
if (node is Node2D node2D)
{
node2D.Hide();
return true;
}
if (node is CanvasItem canvasItem)
{
canvasItem.Hide();
return true;
}
return false;
}
///
/// Sets child nodes for a node
/// 为某个节点设置子节点
///
///
///
public static void CallDeferredAddChild(Node parentNode, Node childNode)
{
parentNode.CallDeferred("add_child", childNode);
}
///
/// Traverse the child nodes of type T under the parent node
/// 遍历父节点下T类型的子节点
///
///
///
///A function that handles callbacks and returns true to terminate the traversal of the node
///用于处理回调的函数,返回true终止遍历节点
///
///
///When the type is specified as Node, all child nodes are returned.
///当指定类型为Node时,将返回所有子节点。
///
public static void ForEachNode(Node parent, Func func) where T : Node
{
var count = parent.GetChildCount();
if (count <= 0)
{
return;
}
for (var i = 0; i < count; i++)
{
var node = parent.GetChild(i);
if (node is not T t) continue;
if (func.Invoke(t))
{
break;
}
}
}
///
/// All child nodes are removed asynchronously
/// 异步删除所有子节点
///
///
///
public static async Task DeleteAllChildAsync(Node parent)
{
return await Task.Run(() => DeleteAllChild(parent));
}
///
/// Gets the node closest to the origin
/// 获取距离原点最近的节点
///
///
///origin
///原点
///
///
///Node array
///节点数组
///
///
///Whether or not unseen nodes should be excluded
///是否排除不可见的节点
///
///
///Filter, which returns true within the function to filter the specified node.
///过滤器,在函数内返回true,则过滤指定节点。
///
///
public static Node2D? GetTheNearestNode(Node2D origin, Node[] array,
bool excludeInvisibleNodes = true, Func? filter = null)
{
var closestDistance = float.MaxValue;
Node2D? closestNode = null;
foreach (var node in array)
{
if (node is not Node2D node2D) continue;
if (excludeInvisibleNodes && !node2D.Visible)
{
//If invisible nodes are excluded and the current node is invisible, then the next.
//如果排除不可见的节点,且当前节点就是不可见的,那么下一个。
continue;
}
if (filter != null && filter.Invoke(node2D))
{
//If there is a filter, and the filter returns true, then the next.
//如果有过滤器,且过滤器返回true,那么下一个。
continue;
}
var distance = node2D.GlobalPosition.DistanceTo(origin.GlobalPosition);
if (distance < closestDistance)
{
closestDistance = distance;
closestNode = node2D;
}
}
return closestNode;
}
///
/// Find the corresponding container node based on the child node
/// 根据子节点查找对应的容器节点
///
///
///We want child nodes to be placed under a specific parent node to facilitate the same management. For example, the weapon node should be placed inside the Weapon container node. We call a parent node of the same type as a child node a "container". This method is used to find the corresponding node container based on the type of the child node.
///我们希望子节点被放置在特定的父节点下,方便同一管理。例如:武器节点应该被放置在“武器容器”节点内。我们将子节点的类型相同的父节点叫做“容器”。此方法用于根据子节点的类型查找对应的节点容器。
///
///
///childNode
///子节点
///
///
///Default parent, which returns the default node if it cannot be matched by type.
///默认父节点,当按照类型无法匹配时,将返回默认节点。
///
///
public static Node FindContainerNode(Node childNode, Node defaultParentNode)
{
if (GameSceneNodeHolder.ProjectileContainer != null && childNode is Projectile)
{
return GameSceneNodeHolder.ProjectileContainer;
}
if (GameSceneNodeHolder.WeaponContainer != null && childNode is WeaponTemplate)
{
return GameSceneNodeHolder.WeaponContainer;
}
if (GameSceneNodeHolder.PacksackContainer != null && childNode is Packsack)
{
return GameSceneNodeHolder.PacksackContainer;
}
if (GameSceneNodeHolder.BackpackUiContainer != null && childNode is PacksackUi)
{
return GameSceneNodeHolder.BackpackUiContainer;
}
return defaultParentNode;
}
///
/// Instantiate the scene and transform it into a node of the target type
/// 实例化场景并将其转换为目标类型的节点
///
///
///packedScene
///打包的场景
///
///
///genericity
///泛型
///
///
///If the returned type is the target type, the node converted to the target type is returned, otherwise null is returned
///如果返回的类型是目标类型,那么返回转换到目标类型的节点,否则返回null
///
public static T? InstantiatePackedScene(PackedScene packedScene)
where T : class
{
var node = packedScene.Instantiate();
// Check the type conversion and return the result successfully
// 检查类型转化,成功返回结果
if (node is T result) return result;
// If the transformation fails, release the created node
//如果转型失败,释放所创建的节点
LogCat.LogWarningWithFormat("warning_node_cannot_cast_to", LogCat.UploadFormat, LogCat.LogLabel.Default, node,
nameof(T));
node.QueueFree();
return null;
}
}