using Facepunch.ActionGraphs; using Sandbox.ActionGraphs; using System; using System.Text.Json.Nodes; namespace Sandbox; public static partial class SceneExtensions { /// /// We should make this globally reachanle at some point. Should be able to draw icons using bitmaps etc too. /// public static Editor.Menu CreateContextMenu( this Scene scene, Widget parent = null ) { var menu = new Editor.Menu( parent ); menu.AddOption( "Save", "save", action: () => scene.Editor?.Save( false ) ).Enabled = (scene.Editor?.HasUnsavedChanges ?? false) && scene.Source is not null; menu.AddOption( "Save Scene As..", action: () => scene.Editor?.Save( true ) ); return menu; } /// /// Copy the target to the clipboard. /// /// public static void CopyToClipboard( this Component component ) { using var scope = SceneEditorSession.Scope(); var result = component.Serialize(); if ( result is null ) return; EditorUtility.Clipboard.Copy( result.ToString() ); } /// /// Paste component values from clipboard to the target . /// /// public static void PasteValues( this Component target ) { var text = EditorUtility.Clipboard.Paste(); using var scope = SceneEditorSession.Scope(); try { if ( JsonNode.Parse( text ) is not JsonObject pastedJso ) return; using ( SceneEditorSession.Active.UndoScope( "Paste Component Values" ).WithComponentChanges( target ).Push() ) { pastedJso.AsObject().Remove( "__guid" ); target.DeserializeImmediately( pastedJso ); } } catch { // Do nothing. } } /// /// Return true if this object should be shown in the GameObject list /// public static bool ShouldShowInHierarchy( this GameObject target ) { if ( target is null ) return false; if ( target.Flags.Contains( GameObjectFlags.Hidden ) ) return false; return true; } /// /// Paste a as a new component on the target . /// /// public static void PasteComponent( this GameObject target ) { var text = EditorUtility.Clipboard.Paste(); using var scope = SceneEditorSession.Scope(); try { if ( JsonNode.Parse( text ) is not JsonObject pastedJso ) return; var componentType = TypeLibrary.GetType( (string)pastedJso["__type"] ); if ( componentType is null ) { Log.Warning( $"TypeLibrary couldn't find {nameof( Component )} type {pastedJso["__type"]}" ); return; } using ( SceneEditorSession.Active.UndoScope( $"Paste {componentType.Name} As New" ).WithComponentCreations().Push() ) { SceneUtility.MakeIdGuidsUnique( pastedJso ); var cmp = target.Components.Create( componentType ); cmp.DeserializeImmediately( pastedJso ); } } catch { // Do nothing. } } public static void PaintComponentIcon( this TypeDescription td, Rect rect, float opacity = 1 ) { Paint.SetPen( Theme.Green.WithAlpha( opacity ) ); Paint.DrawIcon( rect, td.Icon, rect.Height, TextFlag.Center ); } public static void EnableEditorRigidBody( this Scene scene, Rigidbody body, bool enabled ) { var system = scene.GetSystem(); if ( system is null ) return; if ( enabled ) { system.AddRigidBody( body ); } else { system.RemoveRigidBody( body ); } } public static void DisableEditorRigidBodies( this Scene scene ) { var system = scene.GetSystem(); if ( system is null ) return; system.RemoveRigidBodies(); } public static void EnableEditorPhysics( this Scene scene, bool enabled ) { var system = scene.GetSystem(); if ( system is null ) return; system.Enabled = enabled; } public static void SetTargetTransform( this Rigidbody body, Transform? tx ) { body.TargetTransform = tx; } } /// /// Editor helpers for scene.ref nodes. /// public static class SceneReferenceHelper { public static IReadOnlyDictionary GetNodeProperties( GameObject go ) { return new Dictionary { { "gameobject", GameObjectReference.FromInstance( go ) } }; } public static IReadOnlyDictionary GetNodeProperties( Component component ) { var t = TypeLibrary.GetType( component.GetType() ); return new Dictionary { { "component", ComponentReference.FromInstance( component ) } }; } public static IReadOnlyDictionary GetNodeProperties( string prefabPath ) { return new Dictionary { { "gameobject", GameObjectReference.FromPrefabPath( prefabPath ) } }; } private static Scene GetContainingScene( Node node ) { return (node.ActionGraph.GetEmbeddedTarget() as GameObject)?.Scene ?? Game.ActiveScene; } public static GameObject ResolveTargetGameObject( Node node ) { var scene = GetContainingScene( node ); return node.Properties.TryGetValue( "gameobject", out var prop ) && prop.Value is GameObjectReference goRef ? goRef.Resolve( scene ) : null; } public static Component ResolveTargetComponent( Node node ) { var scene = GetContainingScene( node ); return node.Properties.TryGetValue( "component", out var prop ) && prop.Value is ComponentReference compRef ? compRef.Resolve( scene ) : null; } }