Scene system that scans scene SkinnedModelRenderer components and injects or repairs a PartituraCaras component on nextbot entities tagged "sbh_nextbot". It identifies character name from the model path (models starting with "sprunki_"), sets PartituraCaras.Personaje, and loads/syncs saved score data from PartituraStore.
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;
}
}