Game/DemoCharge.cs
namespace Monolith;

/// <summary>
/// The visible marker for a planted demolition charge. Pulses so it reads as armed and is
/// findable again after you have flown away from it.
/// </summary>
public sealed class DemoCharge : Component
{
	private PointLight light;
	private ModelRenderer renderer;
	private GameTimeSince timeSincePlaced;

	public static DemoCharge Spawn( Scene scene, Vector3 position )
	{
		if ( scene == null ) return null;

		var obj = new GameObject( true, "Demolition Charge" );
		obj.NetworkMode = NetworkMode.Never;
		obj.WorldPosition = position;

		var charge = obj.AddComponent<DemoCharge>();

		// A visible body, not just a light. The whole point of the slow flight is that you
		// watch the charge travel and pick your moment, and a light alone shows nothing once
		// it is inside the rock or crossing empty space.
		var mr = obj.AddComponent<ModelRenderer>();
		mr.Model = Projectile.SharedBoltModel;
		mr.Tint = new Color( 1f, 0.30f, 0.12f );
		obj.WorldScale = new Vector3( 26f, 11f, 11f );

		charge.renderer = mr;

		var pointLight = obj.AddComponent<PointLight>();
		pointLight.LightColor = new Color( 1f, 0.25f, 0.15f ) * 8f;
		pointLight.Radius = 260f;
		pointLight.Shadows = false;

		charge.light = pointLight;
		return charge;
	}

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

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

		if ( !light.IsValid() )
			return;

		// Roughly twice a second, so it is obviously ticking rather than merely lit.
		float pulse = 0.55f + 0.45f * MathF.Sin( timeSincePlaced * 9f );

		light.LightColor = new Color( 1f, 0.25f, 0.15f ) * (5f + 7f * pulse);
		light.Radius = 220f + 80f * pulse;

		if ( renderer.IsValid() )
			renderer.Tint = new Color( 1f, 0.22f + 0.35f * pulse, 0.10f );
	}
}