EfectosNextbot.cs

Component that spawns one-shot particle prefabs for a nextbot on spawn, damage, death and when its attack connects. It binds to Vida and NextbotBrain events, clones configured prefabs at computed positions, and uses a TemporaryEffect component to auto-destroy the cloned root after 5 seconds.

File Access
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;
	}
}