using Editor.MapEditor; using NativeMapDoc; using System; using System.ComponentModel.DataAnnotations; namespace Editor.MapDoc; internal enum MapNodeGetRootDocument { /// /// Will return null if the root document is still being loaded /// MustBeLoaded = 0, /// /// Will return the root document even if it is not completely loaded /// MayBeLoading = 1 }; /// /// A common class used for all objects in the world object tree. /// [Display( Name = "Node" ), Icon( "help_outline" )] public class MapNode : IHandle { #region IHandle // // A pointer to the actual native object // internal CMapNode native; // // IHandle implementation // void IHandle.HandleInit( IntPtr ptr ) => OnNativeInit( ptr ); void IHandle.HandleDestroy() => OnNativeDestroy(); bool IHandle.HandleValid() => !native.IsNull; #endregion internal MapNode() { } internal MapNode( HandleCreationData _ ) { } internal string GizmoId; internal virtual void OnNativeInit( CMapNode ptr ) { native = ptr; GizmoId = Guid.NewGuid().ToString( "N" )[0..8]; } internal virtual void OnNativeDestroy() { native = IntPtr.Zero; } /// /// A new node has been added to the world - does not happen when loaded from file /// internal virtual void OnAddedToWorld( MapWorld world ) { } internal virtual void OnRemovedFromWorld( MapWorld world ) { } internal virtual void PreSaveToFile() { } internal virtual void PostLoadFromFile() { } internal virtual void OnNativeTransformChanged( Vector3 position, Angles angle, Vector3 scale ) { } internal virtual void OnSetEnabled( bool enabled ) { } internal virtual void GetMimeData( DragData data ) { } internal virtual void OnCopyFrom( MapNode copyFrom, int flags ) { } internal virtual void OnParentChanged( MapNode parent ) { } /// /// User specified name of this node /// public string Name { get => native.GetName(); set { Assert.NotNull( value ); native.SetName( value ); } } /// /// Native C++ type name for this map node (nice for debug, might disappear at some point) /// public string TypeString => native.GetTypeString(); /// /// World position of this map node. /// public Vector3 Position { get => native.GetOrigin(); set => native.SetOrigin( value ); } /// /// Euler angles of this map node. /// public Angles Angles { get => native.GetAngles(); set => native.SetAngles( value ); } /// /// Non-uniform scalar for this map node. /// public Vector3 Scale { get => native.GetScales(); set => native.SetScales( value ); } /// /// The parent node, at the top level this will be the /// public MapNode Parent { get => native.GetParent(); set => native.SetParent( value ); } /// /// Each MapNode can have many children. Children usually transform with their parents, etc. /// public IEnumerable Children { get { // TODO: Ideally we'd have a List in managed that gets updated from hooks, but getting that from mapdoclib is a huge pain. for ( int i = 0; i < native.GetChildCount(); i++ ) { yield return native.GetChild( i ); } } } /// /// Visibility of this MapNode, e.g if it's been hidden by the user /// public bool Visible { get => native.IsVisible(); set => native.SetVisible( value ); } /// /// The world this map node belongs to. /// public MapWorld World { get => native.GetParentWorld(); } /// /// Map node types implement this to describe themselves and their children. /// e.g CMapEntity returns "Entity: entity_name" or CMapMesh returns "Mesh (2 faces)" /// public override string ToString() { return $"MapNode: {native.GetDescription()}"; } /// /// Creates a copy of this map node. /// public MapNode Copy() { var copy = native.Copy(); if ( !copy.IsValid() ) return null; // Automatically add it to the current mapdoc, we don't really deal with them outside of a mapdoc for now Hammer.ActiveMap.native.AddObjectToDocument( copy, native.GetParent() ); return copy; } public void Remove() { var doc = World.worldNative.GetRootDocument( MapNodeGetRootDocument.MayBeLoading ); doc.DeleteNode( this ); } /// /// Does this map node generate models to use? /// public bool GeneratesEntityModelGeometry => native.GeneratesEntityModelGeometry(); }