Component that attaches particle prefabs to a nextbot for spawn, damage, death and when its attack hits. It subscribes to Vida and NextbotBrain events, clones configured particle prefabs at specified positions, and uses a TemporaryEffect component to auto-destroy the cloned root.
namespace SBH;
using Sandbox;
/// <summary>
/// [SBHBase] Efectos de partículas del nextbot: al aparecer, al morir, al
/// recibir daño y cuando su golpe conecta. Cada efecto es un prefab one-shot
/// (emisor con Loop apagado y DestroyOnEnd encendido) que se clona en el
/// lugar y se limpia solo cuando las partículas terminan — mismo patrón que
/// los efectos del rollermine de sandbox. Cualquier campo vacío simplemente
/// no emite nada.
/// Requiere Vida en el mismo GameObject para muerte/daño y NextbotBrain
/// para el golpe conectado; sin ellos, esos efectos quedan mudos.
/// </summary>
public sealed class EfectosNextbot : Component
{
[Property, Description( "Prefab de partículas al aparecer (se clona en los pies)" )]
public GameObject EfectoSpawn { get; set; }
[Property, Description( "Prefab de partículas al morir (se clona en el torso)" )]
public GameObject EfectoMuerte { get; set; }
[Property, Description( "Prefab de partículas al recibir daño, tipo sangre (en el torso; el golpe mortal usa solo el de muerte)" )]
public GameObject EfectoDanio { get; set; }
[Property, Description( "Prefab de partículas cuando SU golpe conecta (se clona en la víctima)" )]
public GameObject EfectoGolpe { get; set; }
[Property, Description( "Altura del torso sobre los pies, donde nacen los efectos de daño y muerte" )]
public float AlturaTorso { get; set; } = 40f;
Vida _vida;
NextbotBrain _cerebro;
Vector3 Torso => WorldPosition + Vector3.Up * AlturaTorso;
protected override void OnEnabled()
{
_vida = Components.Get<Vida>();
_cerebro = Components.Get<NextbotBrain>( true );
if ( _vida.IsValid() )
{
_vida.AlRecibirDanio += AlDaniado;
_vida.AlMorir += AlMorirBot;
}
if ( _cerebro.IsValid() )
_cerebro.AlGolpear += AlConectarGolpe;
}
protected override void OnDisabled()
{
if ( _vida.IsValid() )
{
_vida.AlRecibirDanio -= AlDaniado;
_vida.AlMorir -= AlMorirBot;
}
if ( _cerebro.IsValid() )
_cerebro.AlGolpear -= AlConectarGolpe;
}
protected override void OnStart()
{
Clonar( EfectoSpawn, WorldPosition );
}
void AlDaniado( float cantidad )
{
// El golpe mortal ya tiene su propio efecto (AlMorir): no duplicar
if ( _vida.IsValid() && _vida.EstaMuerto ) return;
Clonar( EfectoDanio, Torso );
}
void AlMorirBot()
{
Clonar( EfectoMuerte, Torso );
}
void AlConectarGolpe( IVida victima, Vector3 punto )
{
Clonar( EfectoGolpe, punto );
}
static void Clonar( GameObject prefab, Vector3 posicion )
{
if ( prefab == null || !prefab.IsValid() ) return;
var go = prefab.Clone( posicion );
if ( !go.IsValid() ) return;
// El DestroyOnEnd del emisor limpia las capas de partículas, pero el
// objeto raíz del prefab quedaría huérfano en la escena para siempre
// (TemporaryEffect: el mismo componente del engine que usan los gibs)
go.Components.Create<TemporaryEffect>().DestroyAfterSeconds = 5f;
}
}