perks/PerkBulletFragment.cs

A mythic perk class for a game that causes bullets which hit the ground to fragment into three smaller bullets. It adjusts visual highlight properties, plays a nearby impact sound, spawns a ring of three bullets with reduced damage and lifetime, and marks fragments as starting from the ground.

Networking
using System;
using Sandbox;

[Perk( Rarity.Mythic, disabled: true, alwaysOfferDebug: false )]
public class PerkBulletFragment : Perk
{
	private enum Mod { MinDmg, Radius };

	static PerkBulletFragment()
	{
		//Register<PerkBulletFragment>(
		//	name: "Fragmentation Rounds",
		//	imagePath: "textures/icons/vector/bullet_fragment.png",
		//	description: level => $"When your bullets hit the ground, if they're worth more than {GetValue( level, Mod.MinDmg ).ToString( "0.#" )} dmg, they split into 3 bullets that deal 50% dmg",
		//	upgradeDescription: level => $"+{string.Format( "{0:0.00}", GetValue( level, Mod.Radius, true ) )}m bullet homing range, but you lose {GetValue( level - 1, Mod.MinDmg ).ToString("0.#")}→{GetValue( level, Mod.MinDmg ).ToString("0.#")} hp when they hit the ground"
		//);
	}

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

		HighlightColor = new Color( 0.6f, 0.6f, 0.6f );
		HighlightDuration = 0.2f;
		HighlightOpacity = 2f;
	}

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

	}

	public override void OnBulletHitGround( Bullet bullet )
	{
		base.OnBulletHitGround( bullet );

		if ( bullet.Stats[BulletStat.Damage] > 4f )
		{
			Manager.Instance.PlaySfxNearbyRpc( "bullet.impact", bullet.Position2D, pitch: Game.Random.Float( 1.6f, 1.7f ), volume: 0.75f, maxDist: 500f );

			var bullets = Player.SpawnBulletRing( bullet.Position2D, 3, Utils.GetRandomVector(), isFromClip: false, damageMultMin: 0.5f, damageMultMax: 0.5f, spawnOffset: 2f );
			if ( bullets != null )
			{
				foreach ( var b in bullets )
				{
					b.Stats[BulletStat.StartFromGround] = 1f;
					b.Stats[BulletStat.Lifetime] *= Game.Random.Float( 0.5f, 0.65f );
				}
			}

			Highlight();
		}
	}

	private static float GetValue( int level, Mod mod, bool isPercent = false )
	{
		switch ( mod )
		{
			case Mod.MinDmg:
			default:
				return 6f - 2f * level;
			case Mod.Radius:
				return 1.7f * (isPercent ? 1f : Utils.Meter2Unit);
		}
	}
}