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