perks/PerkDodgeNext.cs

A game perk class 'PerkDodgeNext' that grants the player a guaranteed dodge for the next hit. It manages activation, cooldown timing, UI display colors/values, and applies/removes a player modifier for the dodge effect.

Networking
using Sandbox;
using System;
using System.Numerics;

[Perk( Rarity.Epic, locked: true, alwaysOfferDebug: false, IncludedCategories = new[] { PerkCategory.Dodge })]
public class PerkDodgeNext : Perk
{
	private enum Mod { Cooldown };

	private TimeSince _timeSinceDodge;

	private bool _isActive;


	static PerkDodgeNext()
	{
		Register<PerkDodgeNext>(
			name: "Anticipate",
			imagePath: "textures/icons/vector/dodge_next.png",
			description: level => $"You're guaranteed to\ndodge the next hit\n(cooldown: {GetValue( level, Mod.Cooldown )}s)",
			upgradeDescription: level => $"You're guaranteed to\ndodge the next hit\n(cooldown: {GetValue( level - 1, Mod.Cooldown )}→{GetValue( level, Mod.Cooldown )}s)"
		);
	}

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

		DisplayCooldownColor = new Color( 0.7f, 0.7f, 1f, 0.5f );

		ShouldUpdate = true;
	}

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

		Enable();
	}

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

	}

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

		if ( _isActive )
		{
			DisplayCooldownColor = Color.Lerp( new Color( 0.5f, 0.5f, 1f, 0.75f ), new Color( 0f, 0f, 1f, 0.75f ), 0.5f + Utils.FastSin( RealTime.Now * 24f ) * 0.5f );
			DisplayCooldown = 1f;
		}
		else
		{
			var cooldown = GetValue( Level, Mod.Cooldown );
			if ( _timeSinceDodge > cooldown )
			{
				Enable();
			}
			else
			{
				DisplayCooldown = Utils.Map( _timeSinceDodge, 0f, cooldown, 0f, 1f );
			}
		}
	}

	private static float GetValue( int level, Mod mod, bool isPercent = false )
	{
		switch ( mod )
		{
			case Mod.Cooldown:
			default:
				return 17 - level * 3f;
		}
	}

	void Enable()
	{
		_isActive = true;
		DisplayCooldownColor = new Color( 0.3f, 0.3f, 1f, 0.75f );
		DisplayCooldown = 1f;
		Player.Modify( this, PlayerStat.DodgeNextAttack, 1f, ModifierType.Add );
		//DisplayText = "💨";

		HighlightColor = new Color( 0.5f, 0.5f, 1f );
		HighlightDuration = 0.3f;
		HighlightOpacity = 6f;
		Highlight();
	}

	void Disable()
	{
		_isActive = false;
		DisplayCooldownColor = new Color( 0.7f, 0.7f, 1f, 0.5f );
		DisplayCooldown = 0f;
		//DisplayText = " ";
		Player.StopModifying( this, PlayerStat.DodgeNextAttack );

		HighlightColor = new Color( 0.55f, 0.65f, 1f );
		HighlightDuration = 0.3f;
		HighlightOpacity = 4f;
		Highlight();
	}

	public override void OnDodged( float damage, DamageType damageType, Vector2 dir, Enemy enemySource )
	{
		base.OnDodged( damage, damageType, dir, enemySource );

		if ( !_isActive )
			return;

		_timeSinceDodge = 0f;

		Disable();
	}
}