Code/PartituraInyector.cs

Game system that scans the scene for SkinnedModelRenderer components tagged "sbh_nextbot", extracts a character id from the model path (sprunki_*), and injects or repairs a PartituraCaras component on the GameObject. It loads saved data from PartituraStore and applies it to the component.

File Access
namespace SBH;

using Sandbox;

/// <summary>
/// [SBHBase] Inyecta PartituraCaras a TODO sprunki de la escena (tag
/// "sbh_nextbot" con vmdl sprunki_*), identificando al personaje por el
/// nombre del modelo y cargando su partitura guardada si existe — mismo
/// patrón que RitmoInyector: los prefabs quedan limpios y el componente
/// "ya está en todos" listo para editar desde el menú C.
/// Tras una fusión de carteles (FusionNextbot destruye la partitura vieja)
/// el próximo escaneo re-inyecta la del personaje nuevo.
/// </summary>
public sealed class PartituraInyector : GameObjectSystem
{
	TimeSince _desdeEscaneo;

	public PartituraInyector( Scene scene ) : base( scene )
	{
		Listen( Stage.StartUpdate, 21, Escanear, "PartituraInyectorSprunki" );
	}

	void Escanear()
	{
		if ( _desdeEscaneo < 1f ) return;
		_desdeEscaneo = 0f;

		foreach ( var renderer in Scene.GetAllComponents<SkinnedModelRenderer>() )
		{
			var go = renderer.GameObject;
			if ( !go.IsValid() || !go.Tags.Has( "sbh_nextbot" ) ) continue;

			var personaje = ExtraerPersonaje( renderer.Model?.ResourcePath );
			if ( personaje == null ) continue;

			var partitura = go.Components.Get<PartituraCaras>( true );

			// Sanar componentes viejos (agregados a mano al prefab, o de antes
			// de una fusión): sin Personaje el editor no les empuja cambios
			if ( partitura != null )
			{
				if ( partitura.Personaje != personaje )
				{
					partitura.Personaje = personaje;
					var datos = PartituraStore.Cargar( personaje );
					if ( datos != null )
						PartituraStore.AplicarA( partitura, datos );
				}
				continue;
			}

			partitura = go.Components.Create<PartituraCaras>();
			partitura.Personaje = personaje;

			var guardada = PartituraStore.Cargar( personaje );
			if ( guardada != null )
				PartituraStore.AplicarA( partitura, guardada );
		}
	}

	/// <summary> "models/sprunkis/sprunki_funbot.vmdl" → "sprunki_funbot". </summary>
	static string ExtraerPersonaje( string rutaModelo )
	{
		if ( string.IsNullOrEmpty( rutaModelo ) ) return null;

		var barra = rutaModelo.LastIndexOf( '/' );
		var nombre = barra >= 0 ? rutaModelo.Substring( barra + 1 ) : rutaModelo;

		var punto = nombre.LastIndexOf( '.' );
		if ( punto >= 0 ) nombre = nombre.Substring( 0, punto );

		if ( !nombre.StartsWith( "sprunki_" ) ) return null;

		// Los personajes horror (sprunki_hrr_*) son personajes COMPLETOS:
		// llevan su propia partitura con su propio nombre, sin compartir
		return nombre;
	}
}