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.
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;
}
}
}