perks/PerkSelfDmgHealthPack.cs

Perk component that grants the player a health pack when they accumulate enough self-inflicted damage. It tracks accumulated self-damage, shows UI cooldown/text, gives a health_pack item and triggers a highlight when thresholds are reached. Values scale by perk level.

Networking
using System;
using Sandbox;

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

	private float _dmgAccum;

	static PerkSelfDmgHealthPack()
	{
		Register<PerkSelfDmgHealthPack>(
			name: "Tasty Scabs",
			imagePath: "textures/icons/vector/self_dmg_healthpack.png",
			description: level => $"+1 healthpack for every [+]{(int)GetValue( level, Mod.DmgReq )}[/+] self dmg taken",
			upgradeDescription: level => $"+1 healthpack for every\n{(int)GetValue( level - 1, Mod.DmgReq )}→{(int)GetValue( level, Mod.DmgReq )} self dmg taken"
		);
	}

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

		DisplayCooldownColor = new Color( 0.3f, 0.7f, 0.3f, 0.75f );

		HighlightColor = new Color( 0f, 1f, 0.7f );
		HighlightDuration = 0.66f;
		HighlightOpacity = 4f;
	}

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

		if ( _dmgAccum > GetValue( Level, Mod.DmgReq ) )
		{
			Player.GiveItemRpc( "health_pack", Utils.GetRandomVectorInCone( -Player.FacingDir ) );

			_dmgAccum -= GetValue( Level, Mod.DmgReq );

			Highlight();
		}

		DisplayText = $"{MathX.CeilToInt( GetValue( Level, Mod.DmgReq ) - _dmgAccum )}";
		DisplayCooldown = Utils.Map( _dmgAccum, 0f, GetValue( Level, Mod.DmgReq ), 0f, 1f );
	}

	private static float GetValue( int level, Mod mod, bool isPercent = false )
	{
		switch ( mod )
		{
			case Mod.DmgReq:
			default:
				return 115f - 15f * 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 ( damageType != DamageType.Self )
			return;

		_dmgAccum += amount;
		if ( _dmgAccum > GetValue( Level, Mod.DmgReq ) )
		{
			Player.GiveItemRpc( "health_pack", Utils.GetRandomVectorInCone( -Player.FacingDir ) );

			_dmgAccum -= GetValue( Level, Mod.DmgReq );
		}

		DisplayText = $"{MathX.CeilToInt( GetValue( Level, Mod.DmgReq ) - _dmgAccum )}";
		DisplayCooldown = 1f - Utils.Map( _dmgAccum, 0f, GetValue( Level, Mod.DmgReq ), 1f, 0f );
	}
}