perks/PerkNumChoicesTimed.cs

A Perk class for the game that grants additional perk choice slots for a limited time, then automatically levels down or removes itself when the time expires. It updates UI display values, applies the numeric perk choice modifier on refresh, and triggers a level-down via the player when its timer elapses.

NetworkingFile Access
using System;
using Sandbox;

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

	private TimeSince _timeSinceEffect;

	static PerkNumChoicesTimed()
	{
		Register<PerkNumChoicesTimed>(
			name: "Shelf Space",
			imagePath: "textures/icons/vector/num_choices_timed.png",
			description: level => $"+{(int)GetValue( level, Mod.NumPerkChoices )} perk {(level > 1 ? "choices" : "choice")}\nLevel-down this perk after {(int)GetValue( level, Mod.TimeLimit )}s",
			upgradeDescription: level => $"+{(int)GetValue( level - 1, Mod.NumPerkChoices )}→{(int)GetValue( level, Mod.NumPerkChoices )} perk choices\nLevel-down this perk after {(int)GetValue( level, Mod.TimeLimit )}s"
		);
	}

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

		DisplayCooldownColor = new Color( 0.4f, 0.5f, 0.5f, 3f );

		HighlightColor = new Color( 1f, 0.3f, 0.3f );
		HighlightDuration = 0.66f;
		HighlightOpacity = 3f;

		ShouldUpdate = true;

		_timeSinceEffect = 0f;
	}

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

		Player.Modify( this, PlayerStat.NumPerkChoices, GetValue( Level, Mod.NumPerkChoices ), ModifierType.Add );
	}

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

		var timeLimit = GetValue( Level, Mod.TimeLimit );

		DisplayCooldown = Utils.Map( _timeSinceEffect, 0f, timeLimit, 1f, 0f );
		DisplayText = $"{(int)Math.Ceiling( timeLimit - _timeSinceEffect )}";

		if ( _timeSinceEffect > timeLimit )
		{
			var type = TypeLibrary.GetType( this.GetType() );
			var newLevel = Level - 1;
			Manager.Instance.Chat.AddLocalChatMessage( $"{Perk.GetRichTextToken( GetType() )} {(newLevel > 0 ? $"Leveled down from {Level}→{newLevel}" : "Removed perk")}", from: "" );

			Highlight();

			_timeSinceEffect = 0f;

			Player.LevelDownPerk( type );
		}
	}

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