perks/PerkArmorStill.cs

A Legendary perk called Blacksmith that grants the player +1 armor item every few seconds while the player is standing still. It tracks a timer, shows UI highlights and scales/rotates the icon when an armor is granted, and computes the delay per perk level.

Networking
using System;
using Sandbox;

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

	private float _timer;


	static PerkArmorStill()
	{
		Register<PerkArmorStill>(
			name: "Blacksmith",
			imagePath: "textures/icons/vector/armor_still.png",
			description: level => $"+1 armor-item every {GetValue( level, Mod.Delay ).ToString("0.#")}s while not moving",
			upgradeDescription: level => $"+1 armor-item every {GetValue( level - 1, Mod.Delay ).ToString( "0.#" )}→{GetValue( level, Mod.Delay ).ToString( "0.#" )}s\nwhile not moving"
		);
	}

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

		ShouldUpdate = true;

		HighlightColor = new Color( 0.9f, 0.9f, 1f );
		HighlightDuration = 0.2f;
		HighlightOpacity = 0.5f;

		DisplayCooldownColor = new Color( 0.4f, 0.4f, 0.8f, 0.8f );
	}

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

	}

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

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

		if ( !Player.IsMoving )
		{
			_timer += dt;
			if ( _timer > GetValue( Level, Mod.Delay ) )
			{
				GiveArmor();
				Highlight();

				IconScale = Game.Random.Float( 1.1f, 1.2f );
				IconAngleOffset = Game.Random.Float( 10f, 15f ) * (Game.Random.Int( 0, 1 ) == 0 ? -1f : 1f);
			}
			else
			{
				IconAngleOffset = Utils.Map( _timer, 0f, GetValue( Level, Mod.Delay ), 0f, -25f, EasingType.QuadIn );
			}
		}
		else
		{
			_timer = Math.Max( _timer -= dt, 0f );
		}

		//DisplayText = Player.Stats[PlayerStat.Armor] > 0f ? $"{MathX.CeilToInt(GetValue(Level, Mod.Delay) - _timer)}" : " ";
		//DisplayCooldown = !Player.IsMoving ? Utils.Map(_timer, 0f, GetValue(Level, Mod.Delay), 1f, 0f) : 0f;
		DisplayCooldown = Utils.Map( _timer, 0f, GetValue( Level, Mod.Delay ), 0f, 1f );
	}

	void GiveArmor()
	{
		Player.GainArmor( 1 );
		_timer = 0f;
	}
}