mirror of
https://github.com/Facepunch/sbox-public.git
synced 2025-12-23 22:48:07 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
219 lines
6.9 KiB
C#
219 lines
6.9 KiB
C#
using Native;
|
|
using System;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
|
|
namespace Editor;
|
|
|
|
public partial class MapClassVariable
|
|
{
|
|
internal void ToNative( CGameDataVariable gd )
|
|
{
|
|
gd.SetName( Name );
|
|
gd.SetLongName( LongName );
|
|
gd.SetDescription( Description );
|
|
gd.SetGroupName( GroupName );
|
|
|
|
// Editor override
|
|
var editor = VariableEditor();
|
|
if ( !string.IsNullOrEmpty( editor ) )
|
|
{
|
|
Metadata.Add( "editor", editor );
|
|
}
|
|
|
|
// Nullable for ModelDoc
|
|
if ( PropertyType != null && Nullable.GetUnderlyingType( PropertyType ) != null )
|
|
{
|
|
Metadata.Add( "nullable", "true" );
|
|
}
|
|
|
|
// Metadata
|
|
// TODO: This could probably be done better
|
|
if ( Metadata.Count > 0 )
|
|
{
|
|
StringBuilder output = new();
|
|
output.AppendLine( "{" );
|
|
foreach ( var kvp in Metadata )
|
|
{
|
|
bool isNotString = bool.TryParse( kvp.Value, out bool _ ) || float.TryParse( kvp.Value, out float _ );
|
|
output.AppendLine( $"\t{kvp.Key} = {(isNotString ? kvp.Value : kvp.Value.QuoteSafe())}" );
|
|
}
|
|
output.AppendLine( "}" );
|
|
|
|
gd.SetMetadataFromString( output.ToString() );
|
|
}
|
|
|
|
// Default value
|
|
var defaultValue = EditorDefaultValue();
|
|
if ( (PropertyType == typeof( int ) || PropertyType == typeof( int? )) && defaultValue is int defVal )
|
|
{
|
|
// idk why valve do this, maybe i can change it
|
|
// This is necessary for types: int, integer, flags, lod_level, node_dest, node_id
|
|
gd.SetDefaultValueNumber( defVal );
|
|
}
|
|
else
|
|
{
|
|
gd.SetDefaultValue( defaultValue.ToString() );
|
|
}
|
|
|
|
// Type
|
|
if ( string.IsNullOrEmpty( PropertyTypeOverride ) )
|
|
{
|
|
gd.SetTypeFromString( SerializedType( PropertyType ) );
|
|
|
|
if ( PropertyType.IsEnum )
|
|
{
|
|
var itemSet = gd.GetItemSet();
|
|
Assert.True( itemSet.IsValid );
|
|
|
|
var isFlags = PropertyType.HasAttribute( typeof( FlagsAttribute ) );
|
|
|
|
int defaultFlags = defaultValue is int @int ? @int : 0;
|
|
|
|
var enumFields = PropertyType.GetFields().Where( e => e.IsLiteral && !e.HasAttribute( typeof( HideAttribute ) ) );
|
|
foreach ( var field in enumFields )
|
|
{
|
|
var name = field.Name.ToTitleCase();
|
|
var value = field.GetRawConstantValue();
|
|
|
|
if ( isFlags )
|
|
{
|
|
itemSet.AddFlagItem( (int)value, name, (defaultFlags & (int)value) != 0 );
|
|
}
|
|
else
|
|
{
|
|
itemSet.AddChoiceItem( value.ToString(), name );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gd.SetTypeFromString( PropertyTypeOverride );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Translates our C# type to a type mapdoc understands.
|
|
/// I doubt we actually need to use a string here for it, but it does make things easier.
|
|
/// e.g an array
|
|
/// </summary>
|
|
internal string SerializedType( Type type )
|
|
{
|
|
if ( type == typeof( int ) ) return "integer";
|
|
if ( type == typeof( float ) ) return "float";
|
|
if ( type == typeof( double ) ) return "float";
|
|
if ( type == typeof( bool ) ) return "boolean";
|
|
if ( type == typeof( Vector3 ) ) return "vector";
|
|
if ( type == typeof( Vector2 ) ) return "vector2";
|
|
if ( type == typeof( string ) ) return "string";
|
|
if ( type == typeof( Rotation ) ) return "angle";
|
|
if ( type == typeof( Angles ) ) return "angle";
|
|
if ( type == typeof( Color ) ) return "color255"; // TODO: this seems fucked, we should have a proper float Color type in Hammer
|
|
if ( type == typeof( Color32 ) ) return "color255";
|
|
if ( type == typeof( RangedFloat ) ) return "vector";
|
|
if ( type == typeof( Material ) ) return "material";
|
|
if ( type == typeof( Model ) ) return "resource:vmdl";
|
|
if ( type.Name == "TagList" ) return "tags";
|
|
if ( type.Name == "SoundEvent" ) return "sound";
|
|
|
|
//
|
|
// The type itself knows it's Resource type
|
|
// e.g Model = resource:vmdl, Texture = resource:vtex, etc..
|
|
//
|
|
var fgdTypeAttr = type.GetCustomAttribute<FGDTypeAttribute>( false );
|
|
if ( fgdTypeAttr != null )
|
|
{
|
|
return fgdTypeAttr.Type;
|
|
}
|
|
|
|
if ( type.IsEnum )
|
|
{
|
|
if ( type.HasAttribute( typeof( FlagsAttribute ) ) ) return "flags";
|
|
return "choices";
|
|
}
|
|
|
|
// Nullable
|
|
var nullableType = Nullable.GetUnderlyingType( type );
|
|
if ( nullableType != null )
|
|
{
|
|
return SerializedType( nullableType );
|
|
}
|
|
|
|
// Game Resources
|
|
var gameRes = type.GetCustomAttribute<AssetTypeAttribute>( false );
|
|
if ( gameRes != null )
|
|
{
|
|
return $"resource:{gameRes.Extension}";
|
|
}
|
|
|
|
Log.Warning( $"{this}: Missing type for: {(type.IsGenericType ? type.GetGenericTypeDefinition() : type.Name)}" );
|
|
return "string";
|
|
}
|
|
|
|
// Should return either an int or a string!
|
|
internal object EditorDefaultValue()
|
|
{
|
|
//
|
|
// For every primitive or struct type in SerializedType
|
|
// we also wanna turn the values into a string Hammer can parse
|
|
//
|
|
if ( DefaultValue is int ) return DefaultValue;
|
|
if ( DefaultValue is float ) return DefaultValue.ToString();
|
|
if ( DefaultValue is double ) return DefaultValue.ToString();
|
|
if ( DefaultValue is bool ) return ((bool)DefaultValue == true ? "1" : "0");
|
|
if ( DefaultValue is string ) return DefaultValue.ToString();
|
|
if ( DefaultValue is Color32 color32 ) return $"{color32.r} {color32.g} {color32.b} {color32.a}";
|
|
if ( DefaultValue is Vector2 vector2 ) return $"{vector2.x} {vector2.y}";
|
|
if ( DefaultValue is Vector3 vector3 ) return $"{vector3.x} {vector3.y} {vector3.z}";
|
|
if ( DefaultValue is Angles angles ) return $"{angles.pitch} {angles.yaw} {angles.roll}";
|
|
if ( DefaultValue != null && DefaultValue.GetType().IsEnum ) return Convert.ToInt32( DefaultValue );
|
|
|
|
// todo: this is fucked, Hammer should have a proper float based color type
|
|
if ( DefaultValue is Color color )
|
|
{
|
|
var color322 = color.ToColor32();
|
|
return $"{color322.r} {color322.g} {color322.b} {color322.a}";
|
|
}
|
|
|
|
if ( DefaultValue is Rotation rotation )
|
|
{
|
|
var rotAngles = rotation.Angles();
|
|
return $"{rotAngles.pitch} {rotAngles.yaw} {rotAngles.roll}";
|
|
}
|
|
|
|
//
|
|
// If the default is not set, deduce it from the property type
|
|
// This is useful for cases like chagning a vector to "0 0 0" and it still counting as changed
|
|
//
|
|
if ( PropertyType == typeof( int ) ) return 0;
|
|
if ( PropertyType == typeof( float ) ) return "0";
|
|
if ( PropertyType == typeof( double ) ) return "0";
|
|
if ( PropertyType == typeof( bool ) ) return "0";
|
|
if ( PropertyType == typeof( Vector3 ) ) return "0 0 0";
|
|
if ( PropertyType == typeof( Vector2 ) ) return "0 0";
|
|
if ( PropertyType == typeof( Rotation ) ) return "0 0 0";
|
|
if ( PropertyType == typeof( Angles ) ) return "0 0 0";
|
|
if ( PropertyType == typeof( Color ) ) return "255 255 255 255"; // TODO: For assets/modeldoc this should be "1 1 1 1"
|
|
if ( PropertyType == typeof( Color32 ) ) return "255 255 255 255";
|
|
//if ( PropertyType == typeof( RangedFloat ) ) return "0 0 0";
|
|
|
|
return "";
|
|
}
|
|
|
|
internal string VariableEditor()
|
|
{
|
|
if ( PropertyType == null ) return null;
|
|
if ( PropertyType == typeof( RangedFloat ) ) return "Ranged()";
|
|
|
|
// The type itself knows the editor
|
|
var fgdTypeAttr = PropertyType.GetCustomAttribute<FGDTypeAttribute>( false );
|
|
if ( fgdTypeAttr != null && !string.IsNullOrEmpty( fgdTypeAttr.Editor ) )
|
|
{
|
|
return fgdTypeAttr.Editor;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|