using Facepunch.ActionGraphs; namespace Sandbox.ActionGraphs; #nullable enable /// /// An from an that references a /// or . /// public readonly record struct SceneReferenceNode( Node Node, GameObject TargetObject, Component? TargetComponent = null ); /// /// Helper methods for action graph editor tools. Mostly workaround for /// and being internal. /// public static class ActionGraphEditorExtensions { /// /// Find all s and s referenced by the given . /// public static IEnumerable GetSceneReferences( this IActionGraphDelegate actionGraphDelegate ) { if ( actionGraphDelegate.GetEmbeddedTarget() is not GameObject { Scene: { } scene, IsValid: true } ) yield break; foreach ( var node in actionGraphDelegate.Graph.Nodes.Values ) { if ( node.GetSceneReference( scene, actionGraphDelegate ) is { } sceneRef ) { yield return sceneRef; } } } public static SceneReferenceNode? GetSceneReference( this Node node, Scene scene, IActionGraphDelegate? actionGraphDelegate = null ) { if ( node.Definition is not SceneRefNodeDefinition ) return null; if ( node.Outputs.Result.Type == typeof( GameObject ) ) { if ( node.GetPropertyOrDefault( "gameobject", actionGraphDelegate ) is not { } goRef ) return null; if ( goRef.Resolve( scene ) is not { } targetObj ) return null; return new SceneReferenceNode( node, targetObj ); } if ( node.GetPropertyOrDefault( "component", actionGraphDelegate ) is not { } cmpRef ) return null; try { if ( cmpRef.Resolve( scene ) is not { } targetCmp ) return null; return new SceneReferenceNode( node, targetCmp.GameObject, targetCmp ); } catch { // Can throw if component type is missing return null; } } private static T? GetPropertyOrDefault( this Node node, string name, IActionGraphDelegate? actionGraphDelegate = null ) where T : struct { if ( !node.Properties.TryGetValue( name, out var property ) ) return default; // IActionGraphDelegate wraps an ActionGraph with some property values overridden. // Look up an overridden property value, defaulting to the original graph's // value for that property. if ( actionGraphDelegate?.Defaults.TryGetValue( $"{node.Id}.{name}", out var rawValue ) is not true ) { rawValue = property.Value; } return rawValue is T typedValue ? typedValue : default; } public static IReadOnlyDictionary GetNodeProperties( GameObject go ) { return new Dictionary { { "gameobject", GameObjectReference.FromInstance( go ) } }; } public static IReadOnlyDictionary GetNodeProperties( string prefabPath ) { return new Dictionary { { "gameobject", GameObjectReference.FromPrefabPath( prefabPath ) } }; } public static IReadOnlyDictionary GetNodeProperties( Component component ) { return new Dictionary { { "component", ComponentReference.FromInstance( component ) } }; } }