A Perk class for a game that can clone a bullet on its first bounce with a chance that scales by level. It registers perk metadata, adjusts highlight visuals, and on bullet bounce may spawn a cloned bullet with similar stats, visuals, and sound effects.
using System;
using Sandbox;
[Perk( Rarity.Legendary, includedAtStart: false, locked: true, alwaysOfferDebug: false )]
public class PerkBulletBounceCopy : Perk
{
private enum Mod { Chance };
static PerkBulletBounceCopy()
{
Register<PerkBulletBounceCopy>(
name: "Scatterbounce",
imagePath: "textures/icons/vector/bullet_bounce_copy.png",
description: level => $"{(int)GetValue( level, Mod.Chance, true )}% chance for bullet-icon\nto clone on first bounce",
upgradeDescription: level => $"{(int)GetValue( level - 1, Mod.Chance, true )}%→{(int)GetValue( level, Mod.Chance, true )}% chance for bullet-icon\nto clone on first bounce"
);
}
public override void Start()
{
base.Start();
HighlightColor = new Color( 0.9f, 0.6f, 0.8f );
HighlightDuration = 0.1f;
HighlightOpacity = 2f;
}
public override void Refresh()
{
base.Refresh();
}
public override void OnBulletBounce( Bullet bullet, Thing other )
{
base.OnBulletBounce( bullet, other );
if ( (int)bullet.Stats[BulletStat.NumBouncing] != bullet.StartingNumBounce - 1 )
return;
if ( Game.Random.Float( 0f, 1f ) > GetValue( Level, Mod.Chance ) )
return;
var dmg = bullet.Stats[BulletStat.Damage];
var dir = Utils.RotateVector( bullet.Velocity.Normal, Game.Random.Float( 15f, 40f ) * (Game.Random.Int( 0, 1 ) == 0 ? -1f : 1f) );
var b = Player.SpawnBullet( bullet.Position2D, dir, dmg, isFromClip: false, bulletType: bullet.BulletType );
b.Stats[BulletStat.NumPiercing] = bullet.Stats[BulletStat.NumPiercing];
b.Stats[BulletStat.NumBouncing] = bullet.StartingNumBounce;
b.Velocity = dir * bullet.Velocity.Length;
if ( bullet.Stats[BulletStat.ArcHeight] > 0f )
b.SetupArc( bullet.Stats[BulletStat.ArcHeight], 0f );
b.Stats[BulletStat.NumBouncing] -= 1;
b.ShowBounce = b.Stats[BulletStat.NumBouncing] > 0f;
// todo: cloned bullet should have same fire/freeze etc instead of random
if ( other.IsValid() )
b.HitThings.Add( other );
Manager.Instance.SpawnRingRpc( bullet.Position2D, Game.Random.Float( 8f, 12f ), new Color( 0f, 1f, 0f, 0.5f ), lifetime: Game.Random.Float( 0.3f, 0.4f ), path: "ring_spiky" );
Manager.Instance.PlaySfxNearbyRpc( "bounce_copy", bullet.Position2D, pitch: Game.Random.Float( 1f, 1.2f ), volume: 2.2f, maxDist: 350f );
//Highlight();
}
private static float GetValue( int level, Mod mod, bool isPercent = false )
{
switch ( mod )
{
case Mod.Chance:
default:
return isPercent
? 3f + 10f * level
: 0.03f + 0.10f * level;
}
}
}