perks/CurseGetCursedOverTime.cs

A unique curse-type perk that periodically gives the player another curse. It tracks time since last curse, updates UI display text and cooldown, and triggers visual highlight and random icon jitter when a new curse is granted.

using System;
using Sandbox;

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

	private TimeSince _timeSinceCurse;

	static CurseGetCursedOverTime()
	{
		Register<CurseGetCursedOverTime>(
			name: "Endless Hex",
			imagePath: "textures/icons/vector/curse_get_cursed_over_time.png",
			description: level => $"[-]+1[/-] curse every [-]{(int)GetValue( level, Mod.Time )}s[/-]"
		);
	}

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

		ShouldUpdate = true;

		HighlightColor = new Color( 0.5f, 0f, 0f );
		HighlightDuration = 0.75f;
		HighlightOpacity = 3.5f;

		DisplayCooldownColor = new Color( 0.75f, 0f, 0f );

		_timeSinceCurse = 0f;
	}

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

	}

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

		var timeReq = GetValue( Level, Mod.Time );

		if(_timeSinceCurse > timeReq)
		{
			Player.GiveRandomPerkItemWithMessage( sourcePerkType: TypeLibrary.GetType( this.GetType() ), curseSelection: CurseSelection.OnlyCurses, forceToCollect: true );

			_timeSinceCurse = 0f;

			Highlight();

			IconScale = Game.Random.Float( 1.2f, 1.3f );
			IconAngleOffset = Game.Random.Float( 10f, 20f ) * (Game.Random.Int( 0, 1 ) == 0 ? -1f : 1f);
		}

		DisplayText = $"{MathX.CeilToInt( timeReq - _timeSinceCurse )}";
		DisplayCooldown = Utils.Map( _timeSinceCurse, 0f, timeReq, 0f, 1f );
	}

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