Components/MountMetadata.cs
/// <summary>
/// Attached to mount-spawned props. This tries to show a graceful bounding box and hints the player what game it's from if they don't have the mount installed.
/// </summary>
[Title( "Mount Metadata" )]
[Category( "Rendering" )]
public sealed class MountMetadata : Component
{
	[Property, Sync( SyncFlags.FromHost )] public string GameTitle { get; set; }
	[Property, Sync( SyncFlags.FromHost )] public Vector3 BoundsSize { get; set; }
	[Property, Sync( SyncFlags.FromHost )] public Vector3 BoundsCenter { get; set; }

	bool _isFallback;

	static Material _material;

	protected override async void OnStart()
	{
		_material ??= Material.Load( "materials/effects/mount_fallback.vmat" );

		var prop = GameObject.GetComponentInChildren<Prop>();
		var modelMissing = prop.IsValid() && !prop.Model.IsValid() && !prop.Model.IsError;
		if ( !modelMissing ) return;

		// Client doesn't have the mount — hide the error model and show fallback overlay
		_isFallback = true;
		foreach ( var mr in GameObject.GetComponentsInChildren<ModelRenderer>() )
		{
			mr.Enabled = false;
		}
	}

	protected override void OnUpdate()
	{
		if ( !_isFallback ) return;

		var bounds = new BBox( BoundsCenter - BoundsSize / 2f, BoundsCenter + BoundsSize / 2f );

		var t = WorldTransform;
		t = new Transform( t.PointToWorld( bounds.Center ), t.Rotation, t.Scale * (bounds.Size / 50f) );

		Game.ActiveScene.DebugOverlay.Model( Model.Cube, transform: t, overlay: false, materialOveride: _material );

		if ( !string.IsNullOrEmpty( GameTitle ) )
		{
			var textPos = WorldPosition + Vector3.Up * (BoundsSize.z / 2f + 8f);
			DebugOverlay.Text( textPos, $"🧩 Install {GameTitle}", color: Color.White, duration: 0f );
		}
	}
}