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; } }