Code/Bounce.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;

namespace Braxnet;

public class Bounce : GameObjectSystem
{
	public static Bounce Instance { get; private set; }

	private readonly Dictionary<string, TimeUntil> _cooldowns = new();

	public Action<string> OnCooldownStart;
	public Action<string> OnCooldownEnd;

	public Bounce( Scene scene ) : base( scene )
	{
		Listen( Stage.FinishFixedUpdate, 10, OnFinishFixedUpdate, "BounceCheck" );
		Instance = this;
	}

	private void OnFinishFixedUpdate()
	{
		foreach ( var (cooldownName, timeUntil) in _cooldowns.ToList() )
		{
			if ( timeUntil )
			{
				_cooldowns.Remove( cooldownName );
				OnCooldownEnd?.Invoke( cooldownName );
			}
		}
	}

	public bool HasCooldown( string key )
	{
		if ( !_cooldowns.ContainsKey( key ) )
		{
			return false;
		}

		return !_cooldowns[key];
	}

	public void SetCooldown( string key, float time )
	{
		_cooldowns[key] = time;
		OnCooldownStart?.Invoke( key );
	}

	public void RemoveCooldown( string key )
	{
		_cooldowns.Remove( key );
	}

	/// <summary>
	///  Returns true if the key is on cooldown, and sets the cooldown if it's not.
	///  Use it like this:
	///  <code>
	///  if ( AutoCooldown( "my_key", 1f ) )
	///   return;
	/// </code>
	/// </summary>
	/// <param name="key"></param>
	/// <param name="time"></param>
	/// <returns></returns>
	public bool AutoCooldown( string key, float time )
	{
		if ( HasCooldown( key ) )
		{
			return true;
		}

		SetCooldown( key, time );
		return false;
	}
}