using NativeEngine; namespace Sandbox; public abstract class MapLoader { /// /// Holds key values for the map object /// public readonly struct ObjectEntry { public string TypeName { get; init; } public string TargetName { get; init; } public string ParentName { get; init; } public Vector3 Position { get; init; } public Angles Angles { get; init; } public Rotation Rotation { get; init; } public Vector3 Scales { get; init; } public Transform Transform { get; init; } public Vector3 WorldOrigin { get; init; } public ITagSet Tags { get; init; } private readonly Dictionary values = new(); internal ObjectEntry( CEntityKeyValues keyValues, Vector3 origin ) { var keyCount = keyValues.GetKeyCount(); for ( int i = 0; i < keyCount; i++ ) { var key = keyValues.GetKey( i ); values.Add( key, keyValues.GetValueString( key, null ) ); } TypeName = GetValueString( "classname", null ); TargetName = GetValueString( "targetname", null ); ParentName = GetValueString( "parentname", null ); WorldOrigin = origin; Position = GetValue( "origin" ) + WorldOrigin; Angles = GetValue( "angles" ); Scales = GetValue( "scales", Vector3.One ); Rotation = Angles.ToRotation(); Transform = new( Position, Rotation, Scales ); var tags = GetValue( "tags" ) ?? string.Empty; Tags = new ReadOnlyTagSet( tags.Split( ",", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries ) ); } private string GetValueString( string key, string defaultValue ) { if ( values.TryGetValue( StringToken.FindOrCreate( key ), out var value ) ) { return value; } return defaultValue; } public readonly T GetValue( string key, T defaultValue = default ) { var value = GetValueString( key, null ); if ( string.IsNullOrWhiteSpace( value ) ) return defaultValue; return (T)value.ToType( typeof( T ) ); } public readonly T GetResource( string key, T defaultValue = default ) where T : Resource { var value = GetValueString( key, null ); if ( string.IsNullOrWhiteSpace( value ) ) return defaultValue; return Resource.Load( typeof( T ), value ) as T; } public readonly string GetString( string key, string defaultValue = default ) { return GetValueString( key, defaultValue ); } } public SceneWorld World { get; private set; } public PhysicsWorld PhysicsWorld { get; private set; } public Vector3 WorldOrigin { get; private set; } protected readonly List SceneObjects = new(); public MapLoader( SceneWorld world, PhysicsWorld physics, Vector3 origin = default ) { World = world; PhysicsWorld = physics; WorldOrigin = origin; } /// /// Create an object from a serialized object entry /// protected abstract void CreateObject( ObjectEntry kv ); internal void CreateEntities( IWorldReference worldRef, string entityLumpName ) { var entityCount = worldRef.GetEntityCount( entityLumpName ); if ( entityCount == 0 ) return; var entries = new List( entityCount ); for ( int i = 0; i < entityCount; ++i ) { var kv = worldRef.GetEntityKeyValues( entityLumpName, i ); if ( !kv.IsValid ) continue; entries.Add( new ObjectEntry( kv, WorldOrigin ) ); } entries.Sort( ( a, b ) => { if ( string.IsNullOrEmpty( a.ParentName ) && string.IsNullOrEmpty( b.ParentName ) ) return 0; if ( string.IsNullOrEmpty( a.ParentName ) ) return -1; if ( string.IsNullOrEmpty( b.ParentName ) ) return 1; if ( a.ParentName == b.TargetName ) return 1; if ( b.ParentName == a.TargetName ) return -1; return string.Compare( a.TargetName, b.TargetName, StringComparison.Ordinal ); } ); foreach ( var kv in entries ) { try { CreateObject( kv ); } catch ( System.Exception e ) { Log.Warning( e, $"Error when trying to create entity ({kv.TypeName})" ); } } } }