perks/CurseHurtLoseXp.cs

A unique curse perk called "Brain Injury" that causes the player to lose XP when hit. It calculates XP loss as a percentage of damage, applies the loss, shows a floater text, and plays a brief highlight animation on the perk icon.

Networking
using System;
using Sandbox;

[Perk( Rarity.Unique, curse: true, alwaysOfferDebug: false )]
public class CurseHurtLoseXp : Perk
{
	private enum Mod { PercentLost };

	static CurseHurtLoseXp()
	{
		Register<CurseHurtLoseXp>(
			name: "Brain Injury",
			imagePath: "textures/icons/vector/curse_hurt_lose_xp.png",
			description: level => $"When hit, lose xp equal to\n[-]{(int)GetValue( level, Mod.PercentLost, true )}%[/-] of the amount"
		);
	}

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

		HighlightColor = new Color( 1f, 0f, 0f );
		HighlightDuration = 0.1f;
		HighlightOpacity = 2f;
	}

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

	}

	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;

		var xpLost = amount * GetValue( Level, Mod.PercentLost );
		var actualXpLost = Math.Min( xpLost, Player.ExperienceCurrent );
		Player.LoseXp( xpLost );

		if ( actualXpLost > 0 )
			Manager.Instance.SpawnFloaterTextRpc( Player.WorldPosition.WithZ( 65f ), $"-{(int)actualXpLost} XP", new Color( 0.6f, 0f, 0.8f ), size: 1f, floaterType: FloaterType.XpLoss );

		Highlight();

		IconScale = Game.Random.Float( 1.1f, 1.15f );
		IconAngleOffset = Game.Random.Float( 5f, 10f ) * (Game.Random.Int( 0, 1 ) == 0 ? -1f : 1f);
	}

	private static float GetValue( int level, Mod mod, bool isPercent = false )
	{
		switch ( mod )
		{
			case Mod.PercentLost:
			default:
				return isPercent
					? 50
					: 0.5f;
		}
	}
}