Effects/DelayedStages.cs
using System;

namespace PlanetMeat;

[Group( "Planet Meat - Effects" ), Title( "Delayed Stages" ), Icon( "hourglass_bottom" )]
public sealed class DelayedStages : Component
{
	public const int STAGE_INDEX_DESTROY = -1;

	[Property]
	public float Delay
	{
		get;
		set
		{
			field = value;
			UntilNext = float.NaN;
		}
	} = 5.0f;
	[Property] public float DestroyAfter { get; set; } = 0.5f;
	[Property] public int NumStages { get; set; } = 1;

	public int StageIndex
	{
		get;
		set
		{
			field = value;

			if ( value < NumStages )
			{
				StageStarted?.Invoke( value );
				UntilNext = Delay;
			}
			else
			{
				StageStarted?.Invoke( STAGE_INDEX_DESTROY );
				UntilNext = DestroyAfter;
				if ( UntilNext < 0.01f )
					GameObject.Destroy();
			}
		}
	} = 0;

	public bool IsEnding => StageIndex >= NumStages;

	private float UntilNext
	{
		get;
		set
		{
			field = value;
			if ( float.IsNaN( value ) )
				Fraction = 0.0f;

			var denom = IsEnding ? DestroyAfter : Delay;
			if ( float.IsNaN( denom ) || denom < 0.01f )
				Fraction = 0.0f;
			else
				Fraction = 1.0f - UntilNext / denom;
		}
	} = float.NaN;
	public float Fraction { get; private set; } = 0.0f;

	public event Action<int> StageStarted;

	protected override void OnUpdate()
	{
		base.OnUpdate();

		if ( float.IsNaN( UntilNext ) )
		{
			UntilNext = (IsEnding ? DestroyAfter : Delay) - Time.Delta;
		}
		else if ( UntilNext > 0.0f )
		{
			UntilNext -= Time.Delta;
			if ( UntilNext <= 0.0f )
			{
				if ( StageIndex >= NumStages )
					GameObject.Destroy();
				else
					StageIndex++;
			}
		}
	}

	public void EndStages()
	{
		if ( StageIndex < NumStages )
			StageIndex = NumStages;
	}

	public void StopTimer()
	{
		UntilNext = float.NaN;
	}
}