mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-03 03:48:24 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
227 lines
5.9 KiB
C#
227 lines
5.9 KiB
C#
using NativeMapDoc;
|
|
using System;
|
|
using System.ComponentModel.DataAnnotations;
|
|
|
|
namespace Editor.MapDoc;
|
|
|
|
[Display( Name = "Map Game Object" ), Icon( "view_in_ar" )]
|
|
public sealed class MapGameObject : MapNode
|
|
{
|
|
internal CMapGameObject mapGameObjectNative;
|
|
|
|
internal MapGameObject( HandleCreationData _ ) { }
|
|
|
|
public GameObject GameObject { get; private set; }
|
|
|
|
public MapGameObject( MapDocument mapDocument = null, GameObject gameObject = null )
|
|
{
|
|
ThreadSafe.AssertIsMainThread();
|
|
|
|
// Default to the active map document if none specificed
|
|
mapDocument ??= MapEditor.Hammer.ActiveMap;
|
|
|
|
Assert.IsValid( mapDocument );
|
|
|
|
GameObject = gameObject;
|
|
|
|
using ( var h = IHandle.MakeNextHandle( this ) )
|
|
{
|
|
mapDocument.native.CreateGameObject( true );
|
|
}
|
|
}
|
|
|
|
internal override void OnNativeInit( CMapNode ptr )
|
|
{
|
|
base.OnNativeInit( ptr );
|
|
mapGameObjectNative = (CMapGameObject)ptr;
|
|
}
|
|
|
|
internal override void OnNativeDestroy()
|
|
{
|
|
base.OnNativeDestroy();
|
|
mapGameObjectNative = default;
|
|
}
|
|
|
|
internal override void OnAddedToWorld( MapWorld world )
|
|
{
|
|
if ( _isInvalid )
|
|
{
|
|
Remove();
|
|
return;
|
|
}
|
|
|
|
// Probably made through new MapGameObject( ... )
|
|
if ( GameObject is not null )
|
|
{
|
|
// GameObject transform is authoritative in this case
|
|
TransformChanged();
|
|
mapGameObjectNative.SetGUID( GameObject.Id.ToString() );
|
|
GameObject.Transform.OnTransformChanged += TransformChanged;
|
|
return;
|
|
}
|
|
|
|
// Loaded, sync to existing gameobject
|
|
var guid = mapGameObjectNative.GetGUID();
|
|
if ( !string.IsNullOrEmpty( guid ) )
|
|
{
|
|
GameObject = world.Scene.Directory.FindByGuid( Guid.Parse( guid ) );
|
|
}
|
|
|
|
using var sceneScope = world.Scene.Push();
|
|
|
|
// Are we copying?
|
|
if ( _toCopyFrom is not null )
|
|
{
|
|
GameObject = _toCopyFrom.Clone();
|
|
_toCopyFrom = null;
|
|
}
|
|
|
|
GameObject ??= new( true );
|
|
GameObject.Transform.OnTransformChanged += TransformChanged;
|
|
|
|
mapGameObjectNative.SetGUID( GameObject.Id.ToString() );
|
|
|
|
// Native has transform authority, make sure it's where it wants it
|
|
GameObject.WorldTransform = new Transform( Position, Rotation.From( Angles ), Scale );
|
|
|
|
// If we have a map mesh, make sure we always have this component
|
|
if ( GeneratesEntityModelGeometry )
|
|
{
|
|
var component = GameObject.GetOrAddComponent<HammerMesh>();
|
|
GameObject.Components.Move( component, -10 ); // Push to top, even though this is very likely the only component
|
|
}
|
|
}
|
|
|
|
internal override void OnRemovedFromWorld( MapWorld world )
|
|
{
|
|
if ( GameObject is null )
|
|
return;
|
|
|
|
GameObject.Transform.OnTransformChanged -= TransformChanged;
|
|
GameObject.DestroyImmediate();
|
|
GameObject = null;
|
|
}
|
|
|
|
private bool _isInvalid;
|
|
internal override void PostLoadFromFile()
|
|
{
|
|
// some sanity
|
|
var guid = mapGameObjectNative.GetGUID();
|
|
if ( string.IsNullOrEmpty( guid ) )
|
|
{
|
|
Log.Warning( $"Loaded {this} that links to no GameObject from the map, something fucked it. Marking for deletion..." );
|
|
_isInvalid = true;
|
|
}
|
|
}
|
|
|
|
internal override void PreSaveToFile()
|
|
{
|
|
mapGameObjectNative.SetGUID( GameObject.Id.ToString() );
|
|
}
|
|
|
|
internal GameObject _toCopyFrom;
|
|
|
|
|
|
/// <summary>
|
|
/// Mapdoc creates a bunch of map nodes that live on the clipboard, undo system or just for transforming.
|
|
/// These will almost never be part of a World until much later, so anything here should be treated as serialization only.
|
|
/// And defer any actual logic until OnAddedToWorld.
|
|
/// It's confusing, but it's how the entire datamodel works foundationally for Hammer and mapdoclib.
|
|
/// </summary>
|
|
internal override void OnCopyFrom( MapNode copyFrom, int flags )
|
|
{
|
|
// There is a special flags = 1 for undo
|
|
// If we are on the undo stack, I think we should store the serialized gameobject
|
|
|
|
if ( copyFrom is not MapGameObject fromMgo )
|
|
return;
|
|
|
|
_toCopyFrom = fromMgo.GameObject;
|
|
|
|
// We might be a copy of a copy...
|
|
if ( fromMgo.GameObject is null )
|
|
_toCopyFrom = fromMgo._toCopyFrom;
|
|
|
|
// Did we copy from an undo node, update position
|
|
if ( GameObject is not null )
|
|
{
|
|
GameObject.WorldTransform = new Transform( Position, Rotation.From( Angles ), Scale );
|
|
}
|
|
}
|
|
|
|
internal override void GetMimeData( DragData data )
|
|
{
|
|
data.Object = new[] { GameObject };
|
|
}
|
|
|
|
internal string GetGameObjectName()
|
|
{
|
|
return GameObject?.Name ?? "";
|
|
}
|
|
|
|
internal override void OnParentChanged( MapNode parent )
|
|
{
|
|
if ( !GameObject.IsValid() ) return;
|
|
|
|
// Maybe jump up until we find a MapGameObject parent
|
|
if ( parent is not MapGameObject parentMgo )
|
|
{
|
|
GameObject.SetParent( null );
|
|
return;
|
|
}
|
|
|
|
var parentGo = parentMgo.GameObject;
|
|
GameObject.SetParent( parentGo );
|
|
}
|
|
|
|
private bool _suppressTransformChanged = false;
|
|
private bool _suppressNativeTransformChanged = false;
|
|
void TransformChanged()
|
|
{
|
|
if ( _suppressTransformChanged ) return;
|
|
|
|
_suppressNativeTransformChanged = true;
|
|
Position = GameObject.WorldPosition;
|
|
Angles = GameObject.WorldRotation.Angles();
|
|
Scale = GameObject.WorldScale;
|
|
_suppressNativeTransformChanged = false;
|
|
|
|
// dispatches: event is when the user has changed core attributes (origin/angles/scales) by typing directly in the property editor
|
|
native.CoreAttributeChanged();
|
|
}
|
|
|
|
internal override void OnNativeTransformChanged( Vector3 position, Angles angle, Vector3 scale )
|
|
{
|
|
if ( _suppressNativeTransformChanged ) return;
|
|
|
|
if ( GameObject is null )
|
|
return;
|
|
|
|
_suppressTransformChanged = true;
|
|
GameObject.WorldPosition = position;
|
|
GameObject.WorldRotation = angle.ToRotation();
|
|
GameObject.WorldScale = scale;
|
|
_suppressTransformChanged = false;
|
|
}
|
|
|
|
internal override void OnSetEnabled( bool enabled )
|
|
{
|
|
if ( GameObject is null )
|
|
return;
|
|
|
|
GameObject.Enabled = enabled;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when meshes are tied to this GameObject
|
|
/// </summary>
|
|
internal void OnMeshesTied()
|
|
{
|
|
if ( GameObject is null )
|
|
return;
|
|
|
|
var component = GameObject.GetOrAddComponent<HammerMesh>();
|
|
GameObject.Components.Move( component, -10 ); // Push to top, even though this is very likely the only component
|
|
}
|
|
}
|