perks/PerkRerollClipDmg.cs

A Legendary perk called "Chambered" that gives temporary bonus damage to bullets for a number of clips after a reroll. It tracks remaining clips, applies a damage bonus on reloads while clips remain, updates display text and cooldown, and resets on removal.

using System;
using Sandbox;

[Perk( Rarity.Legendary, alwaysOfferDebug: false )]
public class PerkRerollClipDmg : Perk
{
	private enum Mod { BonusDmg, NumClips }

	private int _clipsRemaining;

	static PerkRerollClipDmg()
	{
		Register<PerkRerollClipDmg>(
			name: "Chambered",
			imagePath: "textures/icons/vector/reroll_clip_dmg.png",
			description: level =>
			{
				int clips = (int)GetValue( level, Mod.NumClips );
				string clipText = clips == 1 ? "clip" : $"{clips} clips";
				return $"+{GetValue( level, Mod.BonusDmg )} bullet-icon dmg for\nyour next {clipText} on reroll\n(doesn't stack)";
			},
			upgradeDescription: level => $"+{GetValue( level, Mod.BonusDmg )} bullet-icon dmg for\nyour next {(int)GetValue( level - 1, Mod.NumClips )}→{(int)GetValue( level, Mod.NumClips )} clips on reroll\n(doesn't stack)"
		);
	}

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

		HighlightColor = new Color( 1f, 0.85f, 0.4f );
		HighlightDuration = 0.2f;
		HighlightOpacity = 1.5f;
	}

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

		DisplayCooldownColor = new Color( 0.2f, 0.85f, 1f );
	}

	private static float GetValue( int level, Mod mod )
	{
		switch ( mod )
		{
			case Mod.BonusDmg: return 10f;
			case Mod.NumClips:
			default: return level;
		}
	}

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

		_clipsRemaining = (int)GetValue( Level, Mod.NumClips );
		RefreshDisplayText();
		Highlight();
	}

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

		if ( _clipsRemaining > 0 )
		{
			Player.Stats[PlayerStat.ClipDamageBonus] += GetValue( Level, Mod.BonusDmg );
			_clipsRemaining--;
			RefreshDisplayText();
		}
	}

	void RefreshDisplayText()
	{
		DisplayText = _clipsRemaining > 0 ? $"+{GetValue( Level, Mod.BonusDmg ):0.#}" : " ";

		DisplayCooldown = _clipsRemaining / GetValue( Level, Mod.NumClips );
	}

	public override void Remove( bool restart = false )
	{
		base.Remove( restart );

		Player.Stats[PlayerStat.ClipDamageBonus] = 0f;
		_clipsRemaining = 0;
	}
}