perks/PerkDashRechargeFree.cs

A legendary perk class 'PerkDashRechargeFree' that grants a temporary free dash window after a dash recharge. It tracks timers to show a UI window with flashing color/scale, grants a temporary dash (NumTempDashes) when activated, closes the window on dash use or timeout, and plays a UI sound when the window closes.

NetworkingFile Access
using System;

[Perk( Rarity.Legendary, locked: true, alwaysOfferDebug: false )]
public class PerkDashRechargeFree : Perk
{
	private float _timer;
	private float _pulseTimer;
	private bool _windowActive;

	static PerkDashRechargeFree()
	{
		Register<PerkDashRechargeFree>(
			name: "Second Wind",
			imagePath: "textures/icons/vector/dash_recharge_free.png",
			description: level => $"After a dash recharges,\nyou have {GetDuration( level ).ToString("0.##")}s to dash for free",
			upgradeDescription: level => $"After a dash recharges, you have\n{GetDuration( level - 1 ).ToString( "0.##" )}→{GetDuration( level ).ToString( "0.##" )}s to dash for free"
		);
	}

	public override void Start()
	{
		base.Start();
	}

	public override void Update( float dt )
	{
		base.Update( dt );

		_timer += dt;
		_pulseTimer += RealTime.Delta;

		var duration = GetDuration( Level );

		if ( _timer >= duration )
		{
			CloseWindow();
			return;
		}

		DisplayCooldown = Utils.Map( _timer, 0f, duration, 1f, 0f );

		// Flash between cyan and yellow
		float flash = 0.5f + 0.5f * Utils.FastSin( _pulseTimer * MathF.PI * 12f );
		DisplayCooldownColor = Color.Lerp( new Color( 0.2f, 0.9f, 1f, 3f ), new Color( 1f, 0.95f, 0.2f, 3f ), flash );

		// Pulse icon scale
		IconScale = 1f + 0.1f * MathF.Abs( Utils.FastSin( _pulseTimer * MathF.PI * 5f ) );
	}

	public override void OnDashRecharged()
	{
		base.OnDashRecharged();

		_timer = 0f;
		_pulseTimer = 0f;
		ShouldUpdate = true;

		if ( !_windowActive )
		{
			_windowActive = true;
			Player.Modify( this, PlayerStat.NumTempDashes, 1, ModifierType.Add );
		}

		IconScale = 1.3f;
		HighlightColor = new Color( 0.2f, 0.9f, 1f );
		HighlightDuration = 0.4f;
		HighlightOpacity = 4f;
		Highlight();

		DisplayCooldownColor = new Color( 0.2f, 0.9f, 1f, 3f );
	}

	public override void OnDashStarted( Vector2 dir )
	{
		base.OnDashStarted( dir );

		if ( _windowActive )
		{
			CloseWindow();
		}
	}

	private void CloseWindow()
	{
		if ( !_windowActive )
			return;

		_windowActive = false;
		ShouldUpdate = false;
		DisplayCooldown = 0f;
		Player.Modify( this, PlayerStat.NumTempDashes, 0, ModifierType.Add );

		Manager.Instance.PlaySfxUI( "error2", pitch: 0.25f, volume: 0.55f );
	}

	private static float GetDuration( int level )
	{
		return level * 0.5f;
	}
}