perks/PerkPacifistAssassin.cs

A legendary perk class named Precise Assassin that increases the player's bullet damage over time while they avoid kills. It increments a stacked damage buff once per second up to a level-based max, applies it to PlayerStat.BulletDamage, resets on a kill, and updates UI display values.

NetworkingFile Access
using System;
using Sandbox;

[Perk( Rarity.Legendary, locked: true, minUnlocksReq: 1, alwaysOfferDebug: false )]
public class PerkPacifistAssassin : Perk
{
	private enum Mod { Damage, MaxDamage };

	private TimeSince _timeSince;
	private float _currDmgIncrease;

	static PerkPacifistAssassin()
	{
		Register<PerkPacifistAssassin>(
			name: "Precise Assassin",
			imagePath: "textures/icons/vector/pacifist_assassin.png",
			description: level => $"+{GetValue( level, Mod.Damage ).ToString( "0.#" )} bullet-icon dmg/s until you kill\n(max: +{(int)GetValue( level, Mod.MaxDamage )})",
			upgradeDescription: level => $"+{GetValue( level - 1, Mod.Damage ).ToString("0.#")}→{GetValue( level, Mod.Damage ).ToString("0.#")} bullet-icon dmg/s until you kill\n(max: +{(int)GetValue( level - 1, Mod.MaxDamage )}→{(int)GetValue( level, Mod.MaxDamage )})"
		);
	}

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

		ShouldUpdate = true;
		_timeSince = 0f;
		DisplayCooldownColor = new Color( 0.5f, 0.5f, 1f, 3f );

		HighlightColor = new Color( 1f, 0.5f, 0.5f );
		HighlightDuration = 0.3f;
		HighlightOpacity = 1.5f;
	}

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

	}

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

		if ( _timeSince > 1f )
		{
			_currDmgIncrease += GetValue( Level, Mod.Damage );

			if ( _currDmgIncrease > GetValue( Level, Mod.MaxDamage ) )
				_currDmgIncrease = GetValue( Level, Mod.MaxDamage );

			RefreshBuffs();
			_timeSince = 0f;
		}
	}

	void RefreshBuffs()
	{
		Player.Modify( this, PlayerStat.BulletDamage, _currDmgIncrease, ModifierType.Add );
		DisplayText = _currDmgIncrease > 0f ? $"+{string.Format( "{0:0.0}", _currDmgIncrease )}" : " ";
		DisplayCooldown = Utils.Map( _currDmgIncrease, 0f, GetValue( Level, Mod.MaxDamage ), 0f, 1f );
	}

	public override void OnKill( Enemy enemy, DamageType damageType, bool countsAsKill )
	{
		base.OnKill( enemy, damageType, countsAsKill );

		if ( !countsAsKill )
			return;

		_currDmgIncrease = 0f;
		RefreshBuffs();
		_timeSince = 0f;

		Highlight();
	}

	private static float GetValue( int level, Mod mod, bool isPercent = false )
	{
		switch ( mod )
		{
			case Mod.Damage:
			default:
				return 0.1f * level;
			case Mod.MaxDamage:
				return level == 1 ? 5f : 10f;
		}
	}
}