perks/PerkStillRandomShoot.cs

A Perk class named PerkStillRandomShoot, rare rarity, that periodically spawns a random-direction bullet while the player is stationary. It tracks a timer, computes delay based on perk level, spawns a bullet, plays a nearby SFX, triggers highlight and a small dodge/duck RPC, and animates an icon scale/angle.

NetworkingFile Access
using System;
using Sandbox;

[Perk( Rarity.Rare, alwaysOfferDebug: false )]
public class PerkStillRandomShoot : Perk
{
	private enum Mod { Delay };

	private float _timer;

	static PerkStillRandomShoot()
	{
		Register<PerkStillRandomShoot>(
			name: "Spray And Pray",
			imagePath: "textures/icons/vector/still_random_shoot.png",
			description: level => $"Launch a bullet-icon every {GetValue( level, Mod.Delay ).ToString( "0.##" )}s\nwhile not moving",
			upgradeDescription: level => $"Launch a bullet-icon every {GetValue( level - 1, Mod.Delay ).ToString( "0.##" )}→{GetValue( level, Mod.Delay ).ToString( "0.##" )}s\nwhile not moving"
		);
	}

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

		ShouldUpdate = true;

		HighlightColor = new Color( 0.6f, 0.6f, 1f );
		HighlightDuration = 0.1f;
		HighlightOpacity = 1f;
	}

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

	}

	private static float GetValue( int level, Mod mod, bool isPercent = false )
	{
		switch ( mod )
		{
			case Mod.Delay:
			default:
				return 0.56f - level * 0.08f - (level == 5 ? 0.03f : 0f);
		}
	}

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

		if ( Player.IsMoving )
		{
			//_timer = 0f;
			return;
		}

		_timer += dt;
		if ( _timer > GetValue( Level, Mod.Delay ) )
		{
			Shoot();
		}
		else
		{
			IconScale = Player.IsMoving ? 1f : Utils.Map( _timer, 0f, GetValue( Level, Mod.Delay ), 1.1f, 0.8f, EasingType.ExpoIn );
		}
			

		DisplayCooldown = Utils.Map( _timer, 0f, GetValue( Level, Mod.Delay ), 0f, 1f );
	}

	public void Shoot()
	{
		var dir = Utils.GetRandomVector();
		var pos = Player.Position2D + dir * Player.BULLET_SPAWN_OFFSET;
		var damage = Player.GetBulletDamage( isFromClip: false, isLastAmmo: false );
		Player.SpawnBullet( pos, dir, damage );
		_timer = 0f;

		Manager.Instance.PlaySfxNearbyRpc( "player.shoot", pos, Game.Random.Float( 1.2f, 1.25f ), volume: 0.75f, maxDist: 300f );

		Highlight();

		Player.DodgeDuckRpc( dir, time: Game.Random.Float( 0.01f, 0.05f ), shouldFlinch: false );

		IconScale = Game.Random.Float( 1.1f, 1.2f );
		IconAngleOffset = Game.Random.Float( 10f, 15f ) * (Game.Random.Int( 0, 1 ) == 0 ? -1f : 1f);
	}
}