perks/PerkStillDamageReduction.cs

A Perk class for the game named PerkStillDamageReduction, called "Iron Stance". It reduces damage taken while the player is not moving, updates UI display values and visuals, and applies a stat modifier for still-damage reduction.

Networking
using System;
using System.Reflection.Emit;
using Sandbox;
using Sandbox.UI;

[Perk( Rarity.Epic, locked: true, alwaysOfferDebug: false )]
public class PerkStillDamageReduction : Perk
{
	private enum Mod { Percent };

	static PerkStillDamageReduction()
	{
		Register<PerkStillDamageReduction>(
			name: "Iron Stance",
			imagePath: "textures/icons/vector/still_damage_reduction.png",
			description: level => $"[+]-{(int)GetValue( level, Mod.Percent, true )}%[/+] dmg taken while not moving",
			upgradeDescription: level => $"[+]-{(int)GetValue( level - 1, Mod.Percent, true )}%→-{(int)GetValue( level, Mod.Percent, true )}%[/+] dmg taken\nwhile not moving"
		);
	}

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

		HighlightColor = new Color( 0.9f, 0.95f, 1f );
		HighlightDuration = 0.25f;
		HighlightOpacity = 1.5f;

		ShouldUpdate = true;

		DisplayCooldownColor = new Color( 0.5f, 0.6f, 0.8f, 1f );
	}

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

		Player.Modify( this, PlayerStat.StillDamageReductionPercent, GetValue( Level, Mod.Percent ), ModifierType.Add );
	}

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

		DisplayText = Player.IsMoving ? " " : $"-{(int)GetValue( Level, Mod.Percent, true )}%";
		DisplayCooldown = Player.IsMoving ? 0f : 1f;

		if ( IconScale > 0.975f )
			IconScale = 1f + (Utils.FastSin( RealTime.Now * 18f ) * 0.025f) * (Player.IsMoving ? 0f : 1f);
	}

	private static float GetValue( int level, Mod mod, bool isPercent = false )
	{
		switch ( mod )
		{
			case Mod.Percent:
			default:
				return isPercent
					? 6f + 6f * level
					: 0.06f + 0.06f * level;
		}
	}

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

		if ( Player.IsMoving )
			return;

		Highlight();

		IconScale = Game.Random.Float( 0.75f, 0.8f);
		IconAngleOffset = Game.Random.Float( 2f, 5f ) * (Game.Random.Int( 0, 1 ) == 0 ? -1f : 1f);
	}
}