perks/PerkFearArmor.cs

A mythic perk class 'PerkFearArmor' that grants a temporary melee-damage reduction and scares melee attackers. It tracks activation/cooldown, updates UI display values, applies player stat modifiers while active, and triggers an enemy Fear effect when hit by melee, then starts cooldown.

Networking
using System;
using Sandbox;

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

	public bool IsActive { get; private set; }
	private TimeSince _timeSinceActivate;

	// todo: particle to show when it's active


	static PerkFearArmor()
	{
		Register<PerkFearArmor>(
			name: "Scary Shield",
			imagePath: "textures/icons/vector/fear_armor.png",
			description: level => $"[+]-50%[/+] melee dmg taken\nand scare the enemy\n(cooldown: {GetValue( level, Mod.Cooldown ).ToString("0.#")}s)",
			upgradeDescription: level => $"[+]-50%[/+] melee dmg taken\nand scare the enemy\n(cooldown: {GetValue( level - 1, Mod.Cooldown ).ToString("0.#")}→{GetValue( level, Mod.Cooldown ).ToString("0.#")}s)"
		);
	}

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

		HighlightColor = new Color( 0f, 0f, 0f );
		HighlightDuration = 0.2f;
		HighlightOpacity = 3f;
	}

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

		IsActive = true;
	}

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

		Player.Modify( this, PlayerStat.FearOnMeleeCooldownDisplay, GetValue( Level, Mod.Cooldown ), ModifierType.Add );
		Player.Modify( this, PlayerStat.FearArmorActive, 1f, ModifierType.Add );
	}

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

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

		if ( _timeSinceActivate > cooldown )
		{
			IsActive = true;
			Player.Modify( this, PlayerStat.FearArmorActive, 1f, ModifierType.Add );
			ShouldUpdate = false;
			DisplayText = " ";
			DisplayCooldown = 0f;
		}
		else
		{
			DisplayCooldown = Utils.Map( _timeSinceActivate, 0f, cooldown, 1f, 0f );
		}
	}

	private static float GetValue( int level, Mod mod, bool isPercent = false )
	{
		switch ( mod )
		{
			case Mod.Cooldown:
			default:
				return 5f - 1f * 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 ( !IsActive )
			return;

		if ( Player.IsDamageTypeMelee( damageType ) && enemySource.IsValid() && !enemySource.IsDying )
		{
			enemySource.Fear( Player, enemySource: null, lifetime: Player.Stats[PlayerStat.FearLifetime] );

			_timeSinceActivate = 0f;
			IsActive = false;
			Player.StopModifying( this, PlayerStat.FearArmorActive );
			ShouldUpdate = true;
			DisplayText = "❌";

			Highlight();
		}
	}
}