perks/PerkArmorHurtHp.cs

A Perk class named PerkArmorHurtHp (Bloodsteel). When the player loses HP and the perk is ready, it grants that much armor (integer of the damage amount) and starts a cooldown; it updates UI cooldown, highlight and icon effects.

Networking
using System;
using Sandbox;

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

	private bool _isReady = true;
	private TimeSince _timeSinceActivate;


	static PerkArmorHurtHp()
	{
		Register<PerkArmorHurtHp>(
			name: "Bloodsteel",
			imagePath: "textures/icons/vector/armor_hurt_hp.png",
			description: level => $"When you lose hp,\nget that much armor-item\n(cooldown: {GetValue( level, Mod.Cooldown ).ToString( "0.#" )}s)",
			upgradeDescription: level => $"When you lose hp,\nget that much armor-item\n(cooldown: {GetValue( level - 1, Mod.Cooldown ).ToString( "0.#" )}→{GetValue( level, Mod.Cooldown ).ToString( "0.#" )}s)"
		);
	}

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

	}

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

		if ( Level > 1 && !_isReady )
			ReadyFx();

		_isReady = true;
		ShouldUpdate = false;
		DisplayText = " ";
		DisplayCooldown = 0f;
	}

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

	}

	void ReadyFx()
	{
		Manager.Instance.PlaySfxUI( "armor_hurt_hp_ready", pitch: Game.Random.Float( 0.95f, 1.05f ), volume: 0.75f );

		HighlightColor = new Color( 0.7f, 0.5f, 1f, 0.75f );
		HighlightDuration = 0.25f;
		HighlightOpacity = 3f;
		Highlight();

		IconScale = Game.Random.Float( 1.2f, 1.3f );
		IconAngleOffset = Game.Random.Float( 10f, 20f ) * (Game.Random.Int( 0, 1 ) == 0 ? -1f : 1f);

		// todo: show particle vfx or something
	}

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

		float cooldown = GetValue( Level, Mod.Cooldown );

		if ( _timeSinceActivate > cooldown )
		{
			_isReady = true;
			ShouldUpdate = false;
			DisplayText = " ";
			DisplayCooldown = 0f;

			ReadyFx();
		}
		else
		{
			DisplayCooldown = Utils.Map( _timeSinceActivate, 0f, cooldown, 1f, 0f );
			DisplayText = $"{MathX.CeilToInt( cooldown - _timeSinceActivate )}";
		}
	}

	public override void OnHurt( float amount, DamageType damageType, bool isSelfInflicted, Vector2 dir, Enemy enemySource, EnemyType enemyType, float previousHealth )
	{
		base.OnHurt( amount, damageType, isSelfInflicted, dir, enemySource, enemyType, previousHealth );

		if ( !_isReady || amount <= 0f )
			return;

		Player.GainArmor( (int)amount );

		_timeSinceActivate = 0f;
		_isReady = false;
		ShouldUpdate = true;

		HighlightColor = new Color( 0.7f, 0.5f, 1f, 0.75f );
		HighlightDuration = 0.3f;
		HighlightOpacity = 3f;
		Highlight();

		IconScale = Game.Random.Float( 1.2f, 1.3f );
		IconAngleOffset = Game.Random.Float( 10f, 20f ) * (Game.Random.Int( 0, 1 ) == 0 ? -1f : 1f);
	}

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