perks/PerkDoubleDashes.cs

A unique perk class 'PerkDoubleDashes' that triples a player's dashes and tracks hits; after 9 hits it forces the player to die. It updates display text, plays floating text and sound effects, and modifies player dash stat while active.

Networking
using System;
using Sandbox;

[Perk( Rarity.Unique, locked: true, alwaysOfferDebug: false )]
public class PerkDoubleDashes : Perk
{
	private int _numTimesHit;

	static PerkDoubleDashes()
	{
		Register<PerkDoubleDashes>(
			name: "Feline Aspect",
			imagePath: "textures/icons/vector/double_dashes.png",
			description: level => $"Triple your dashes\nDie after being hit 9 times"
		);
	}

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

		ShouldUpdate = true;

	}

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

		Manager.Instance.SpawnFloaterTextRpc( Player.WorldPosition.WithZ( 65f ), $"9 LIVES LEFT", new Color( 0f, 0.7f, 0.8f ), size: 1.3f, floaterType: FloaterType.NegativeMessage );

		RefreshDisplayText();

		Manager.Instance.PlaySfxNearbyRpc( "cat_begin", Player.Position2D, pitch: Game.Random.Float( 0.9f, 1.1f ), volume: 2f, maxDist: 500f );
	}

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

		Player.Modify( this, PlayerStat.NumDashes, 3f, ModifierType.Mult );

	}

	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.IsDead || damageType == DamageType.Self )
			return;

		_numTimesHit++;
		if ( _numTimesHit >= 9 )
		{
			if( Player.TryDie( dir, upwardAmount: Game.Random.Float(0f, 0.2f), force, enemyType, isSelfInflicted: true, damageType: DamageType.Self, playSfx: false ) )
			{
				// todo: show death reason in chat

				Manager.Instance.PlaySfxNearbyRpc( "cat_die_2", Player.Position2D, pitch: Game.Random.Float( 1.2f, 1.25f ), volume: 3f, maxDist: 500f );
			}

			RefreshDisplayText();
		}
		else
		{
			int livesLeft = 9 - _numTimesHit;
			Color color = Color.Lerp( new Color( 0f, 0.7f, 0.8f ), new Color( 1f, 0.3f, 0.5f ), Utils.Map( livesLeft, 8, 1, 0f, 1f, EasingType.Linear ) );
			float size = Utils.Map( livesLeft, 8, 1, 1.3f, 1.5f, EasingType.QuadIn );
			var str = (livesLeft == 1) ? $"FINAL LIFE" : $"{livesLeft} LIVES LEFT";
			Manager.Instance.SpawnFloaterTextRpc( Player.WorldPosition.WithZ( 65f ), str, color, size, floaterType: FloaterType.NegativeMessage );

			RefreshDisplayText();
			Manager.Instance.PlaySfxNearbyRpc( "cat_die", Player.Position2D, pitch: Game.Random.Float( 0.85f, 1.2f ), volume: 3.5f, maxDist: 400f );
		}
	}

	void RefreshDisplayText()
	{
		var amount = 9 - _numTimesHit;
		DisplayText = $"{amount}";
		DisplayTextColor = Color.Lerp( new Color( 1f, 0.6f, 0.6f ), new Color( 1f, 0f, 1f ), Utils.Map( amount, 9, 1, 0f, 1f ) );
		DisplayTextOpacity = Utils.Map( amount, 9, 1, 3f, 4f );
		DisplayCooldown = Utils.Map( amount, 9, 0, 0f, 1f );
		DisplayCooldownColor = Color.Lerp( new Color( 0.3f, 0f, 0f ), new Color( 0.5f, 0f, 0.5f ), Utils.Map( amount, 9, 0, 0f, 1f ) );
	}
}