#if TOOLS using Godot; namespace Rokojori.Tools { // scenePath is a list of child names from the scene root, e.g. ["Player","CharacterBody3D"]. // SceneState has no parent/child API - nodes are a flat list, each with its own full relative // path string (root is "."), so matching is by path-string comparison / prefix. public static class ScenePathResolver { // SceneState.GetNodePath(idx, false) stringifies relative to the scene root as "./Name/Child", // i.e. with an explicit "." leading segment - not a bare "Name/Child" as a naive join would produce. public static string ToRelativePath( string[] scenePath ) { if ( scenePath == null || scenePath.Length == 0 ) { return "."; } return "./" + string.Join( "/", scenePath ); } public static int FindNodeIndex( SceneState state, string relativePath ) { var count = state.GetNodeCount(); for ( var i = 0; i < count; i++ ) { if ( state.GetNodePath( i, false ).ToString() == relativePath ) { return i; } } return -1; } // 0 = same node, 1 = direct child, 2 = grandchild, etc. -1 = not a descendant. public static int DepthOf( string rootRelativePath, string candidateRelativePath ) { if ( rootRelativePath == candidateRelativePath ) { return 0; } var prefix = rootRelativePath == "." ? "./" : rootRelativePath + "/"; if ( ! candidateRelativePath.StartsWith( prefix ) ) { return -1; } var remainder = candidateRelativePath.Substring( prefix.Length ); return remainder.Split( '/' ).Length; } public static Node FindNode( Node root, string[] scenePath ) { if ( scenePath == null || scenePath.Length == 0 ) { return root; } return root.GetNodeOrNull( new NodePath( string.Join( "/", scenePath ) ) ); } public static string[] ToStringArray( Rokojori.JSONArray array ) { if ( array == null ) { return new string[ 0 ]; } var result = new string[ array.size ]; for ( var i = 0; i < array.size; i++ ) { result[ i ] = array.Get( i ).stringValue; } return result; } } } #endif