perks/PerkCopyTwice.cs

A unique perk component named PerkCopyTwice (displayed as "Hat Trick") that duplicates the next non-curse perk the player receives. It tracks state, shows UI highlights and icons, and when the next perk is added it spawns an extra copy shortly after.

Reflection
using System;
using Sandbox;

[Perk( Rarity.Unique, alwaysOfferDebug: false )]
public class PerkCopyTwice : Perk
{
	private TypeDescription _type;
	private bool _ready;
	private bool _hasGiven;
	private float _getTime;

	public override float ImportanceMultiplier => 0f;

	static PerkCopyTwice()
	{
		Register<PerkCopyTwice>(
			name: "Hat Trick",
			imagePath: "textures/icons/vector/copy_twice.png",
			description: level => $"Duplicate the next\nperk you get twice"
		);
	}

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

		HighlightColor = new Color( 1f, 1f, 1f );
		HighlightDuration = 0.4f;
		HighlightOpacity = 6f;
	}

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

		_ready = false;
		_hasGiven = false;
		DisplayText = "✌️";
		ShouldUpdate = true;
		DisplayCooldown = 1f;

		// todo: give a random curse?
	}

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

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

		DisplayCooldownColor = new Color( 0.3f, 0.3f, 1f, Utils.Map( Utils.FastSin( RealTime.Now * 12f ), -1f, 1f, 0f, 1f ) );

		if ( _ready && Time.Now - _getTime > 0.3f )
		{
			Player.GivePerkItem( _type, dir: Utils.GetRandomVectorInCone(-Player.FacingDir ) );
			_ready = false;
			ShouldUpdate = false;
			DisplayText = " ";
			DisplayCooldown = 0f;

			Highlight();
		}
	}

	public override void OnAddPerkAfter( TypeDescription type )
	{
		base.OnAddPerkAfter( type );

		if ( _hasGiven )
			return;

		if ( type.Equals( TypeLibrary.GetType( typeof( PerkCopyTwice ) ) ) )
			return;

		var attrib = type.GetAttribute<PerkAttribute>();
		if ( attrib.Curse )
			return;

		_ready = true;
		_hasGiven = true;
		Player.GivePerkItem( type, dir: Utils.GetRandomVectorInCone( -Player.FacingDir ) );
		_type = type;
		DisplayText = "☝️";
		_getTime = Time.Now;

		Highlight();

		// todo: chat message?
		//Manager.Instance.Chat.AddLocalChatMessage( "Duplicated your next perk:", from: "", perkType: TypeLibrary.GetType( this.GetType() ), perkTypeEnd: type );
	}
}