perks/PerkPacifistBarrel.cs

A legendary perk class that spawns a Barrel enemy when the player has not killed anything for a configurable time. It tracks time since last kill, updates UI fields (cooldown, text, highlight) and resets on kills.

Networking
using System;
using Sandbox;

[Perk( Rarity.Legendary, locked: true, alwaysOfferDebug: false )]
public class PerkPacifistBarrel : Perk
{
	private enum Mod { Time };

	private TimeSince _timeSince;

	static PerkPacifistBarrel()
	{
		Register<PerkPacifistBarrel>(
			name: "Business Importer",
			imagePath: "textures/icons/vector/pacifist_barrel.png",
			description: level => $"+1 barrel when you don't kill for {(int)GetValue( level, Mod.Time )}s",
			upgradeDescription: level => $"+1 barrel when you\ndon't kill for {(int)GetValue( level - 1, Mod.Time )}→{(int)GetValue( level, Mod.Time )}s"
		);
	}

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

		ShouldUpdate = true;
		_timeSince = 0f;
		DisplayCooldownColor = new Color( 1f, 1f, 1f, 0.3f );
	}

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

	}

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

		if ( _timeSince > GetValue( Level, Mod.Time ) )
		{
			var pos = Player.Position2D + Utils.GetRandomVector() * Game.Random.Float( 10f, 30f );

			Manager.Instance.SpawnEnemyRpc( EnemyType.Barrel, pos );

			Manager.Instance.PlaySfxNearbyRpc( "barrel_perk", pos, pitch: Game.Random.Float( 1f, 1.1f ), volume: 1.5f, maxDist: 400f );

			_timeSince = 0f;
			DisplayCooldown = 1f;

			HighlightColor = new Color( 0.8f, 0.8f, 1f );
			HighlightDuration = 0.75f;
			HighlightOpacity = 4f;
			Highlight();
		}
		else
		{
			DisplayCooldown = Utils.Map( _timeSince, 0f, GetValue( Level, Mod.Time ), 0f, 1f );
		}

		DisplayText = $"{Math.Round( GetValue( Level, Mod.Time ) - _timeSince )}";
	}

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

		if ( !countsAsKill )
			return;

		_timeSince = 0f;

		HighlightColor = new Color( 1f, 0.4f, 0.4f );
		HighlightDuration = 0.25f;
		HighlightOpacity = 2f;
		Highlight();
	}

	private static float GetValue( int level, Mod mod, bool isPercent = false )
	{
		switch ( mod )
		{
			case Mod.Time:
			default:
				return 20f - 5f * level;
		}
	}
}