Game/BlastEffect.cs
namespace Monolith;

/// <summary>
/// A brief light flash where a blast landed. This fires up to twenty times a second per player
/// at high upgrade levels, so it is deliberately one GameObject with one light and no particles.
/// </summary>
public sealed class BlastEffect : Component
{
	[Property] public float Lifetime { get; set; } = 0.18f;
	[Property] public float StartRadius { get; set; } = 200f;
	[Property] public float StartBrightness { get; set; } = 6f;

	private PointLight light;
	private Color baseColor = Color.White;
	private GameTimeSince timeSinceSpawn;

	/// <summary>Hard cap so a screen full of drones cannot flood the scene with lights.</summary>
	private static int liveCount;
	private const int MaxLive = 24;

	public static void Spawn( Scene scene, Vector3 position, float worldRadius, bool isCharge )
	{
		if ( scene == null ) return;
		if ( liveCount >= MaxLive ) return;

		var obj = new GameObject( true, "Blast" );
		obj.WorldPosition = position;

		var effect = obj.AddComponent<BlastEffect>();
		effect.StartRadius = MathF.Max( 120f, worldRadius * 3f );
		effect.StartBrightness = isCharge ? 20f : 6f;
		effect.Lifetime = isCharge ? 0.45f : 0.18f;

		effect.baseColor = isCharge
			? new Color( 1f, 0.65f, 0.25f )
			: new Color( 0.75f, 0.88f, 1f );

		var pointLight = obj.AddComponent<PointLight>();
		pointLight.LightColor = effect.baseColor * effect.StartBrightness;
		pointLight.Radius = effect.StartRadius;
		pointLight.Shadows = false;

		effect.light = pointLight;
		liveCount++;
	}

	protected override void OnStart()
	{
		timeSinceSpawn = 0;
	}

	protected override void OnDestroy()
	{
		liveCount = Math.Max( 0, liveCount - 1 );
	}

	protected override void OnUpdate()
	{
		// Frozen while a blocking screen or the pause menu is up. See GameTime.
		if ( GameTime.Paused )
			return;

		float t = timeSinceSpawn / Lifetime;

		if ( t >= 1f )
		{
			GameObject.Destroy();
			return;
		}

		if ( light.IsValid() )
		{
			// Expand and fade. Colour is scaled rather than alpha faded, because light
			// intensity in s&box comes from the magnitude of LightColor.
			float fade = (1f - t) * (1f - t);
			light.Radius = StartRadius * (0.4f + t * 0.6f);
			light.LightColor = baseColor * (StartBrightness * fade);
		}
	}
}