perks/PerkNumChoicesHurt.cs

An Epic rarity perk class named Scar Tissue that grants extra perk choice(s) after the player takes a certain number of hits. It counts hits, converts every N hits (dependent on level) into an available extra choice until the player picks a perk, updates display icon/cooldown and applies/removes a numerical modifier to the player's NumChoicesHurt stat.

Networking
using System;
using Sandbox;

[Perk( Rarity.Epic, locked: true, minUnlocksReq: 2, alwaysOfferDebug: false )]
public class PerkNumChoicesHurt : Perk
{
	private enum Mod { ReqHits };

	private int _hits;
	private int _numChoices;

	static PerkNumChoicesHurt()
	{
		Register<PerkNumChoicesHurt>(
			name: "Scar Tissue",
			imagePath: "textures/icons/vector/num_choices_hurt.png",
			description: level => $"Every {(int)GetValue( level, Mod.ReqHits )} times you take dmg,\nsee +1 perk choice (until you choose)",
			upgradeDescription: level => $"Every {(int)GetValue( level - 1, Mod.ReqHits )}→{(int)GetValue( level, Mod.ReqHits )} times you take dmg,\nsee +1 perk choice (until you choose)"
		);
	}

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

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

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

		CheckHits();
		RefreshIcon();
	}

	void CheckHits()
	{
		var hitsReq = (int)GetValue( Level, Mod.ReqHits );
		if ( _hits >= hitsReq )
		{
			_hits -= hitsReq;
			_numChoices++;

			Player.Modify( this, PlayerStat.NumChoicesHurt, _numChoices, ModifierType.Add );

			HighlightColor = new Color( 0.5f, 0.5f, 1f );
			HighlightDuration = 0.5f;
			HighlightOpacity = 4f;
			Highlight();

			// todo: sfx
		}
	}

	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 );

		_hits++;

		CheckHits();
		RefreshIcon();
	}

	void RefreshIcon()
	{
		DisplayText = _numChoices > 0 ? $"+{_numChoices}" : "";
		DisplayCooldown = Utils.Map( _hits, 0f, GetValue( Level, Mod.ReqHits ), 0f, 1f );
	}

	public override void OnChoosePerk( TypeDescription type )
	{
		base.OnChoosePerk( type );

		_numChoices = 0;
		Player.StopModifying( this, PlayerStat.NumChoicesHurt );

		HighlightColor = new Color( 1f, 0.5f, 0.5f );
		HighlightDuration = 0.75f;
		HighlightOpacity = 4f;
		Highlight();

		RefreshIcon();
	}

	private static float GetValue( int level, Mod mod, bool isPercent = false )
	{
		switch ( mod )
		{
			case Mod.ReqHits:
			default:
				return 16 - level * 2;
		}
	}
}