perks/PerkJumpLandArcBullets.cs

A Perk class for the game that spawns a circular arc of bullet projectiles when the player lands. It configures visuals, computes number of bullets from level, spawns bullets with randomized angle, arc height, speed and lifetime, plays a sound and highlights the perk.

Networking
using System;
using Sandbox;

[Perk( Rarity.Uncommon, includedAtStart: false, locked: true, minUnlocksReq: 7, alwaysOfferDebug: false )]
public class PerkJumpLandArcBullets : Perk
{
	private enum Mod { NumBullets };

	static PerkJumpLandArcBullets()
	{
		Register<PerkJumpLandArcBullets>(
			name: "Landing Barrage",
			imagePath: "textures/icons/vector/jump_land_arc_bullets.png",
			description: level => $"Lob {(int)GetValue( level, Mod.NumBullets )} bullet-icon when you land",
			upgradeDescription: level => $"Lob {(int)GetValue( level - 1, Mod.NumBullets )}→{(int)GetValue( level, Mod.NumBullets )} bullet-icon when you land"
		);
	}

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

		HighlightColor = new Color( 1f, 0.7f, 0.4f );
		HighlightDuration = 0.2f;
		HighlightOpacity = 1f;
	}

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

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

		int numBullets = (int)GetValue( Level, Mod.NumBullets );
		float damage = Player.GetBulletDamage( isFromClip: false, isLastAmmo: false );

		float startAngle = Game.Random.Float( 0f, 360f );
		float angleIncrement = 360f / numBullets;

		for ( int i = 0; i < numBullets; i++ )
		{
			float angle = startAngle + (i * angleIncrement) + Game.Random.Float( -15f, 15f );
			var dir = Utils.GetVector2FromAngleDegrees( angle );
			var pos = Player.Position2D + dir * 10f;
			var bullet = Player.SpawnBullet( pos, dir, damage, isFromClip: false );
			bullet.SetupArc( arcHeight: Game.Random.Float( 150f, 180f ), Player.Stats[PlayerStat.ArcBulletBounces] );
			bullet.Velocity *= Game.Random.Float( 0.35f, 0.55f );
			bullet.Stats[BulletStat.Lifetime] *= Game.Random.Float( 1f, 1.3f );
		}

		Highlight();

		Manager.Instance.PlaySfxNearbyRpc( "player.shoot", Player.Position2D, pitch: 1f, volume: 1f, maxDist: 400f );
	}

	private static float GetValue( int level, Mod mod, bool isPercent = false )
	{
		switch ( mod )
		{
			case Mod.NumBullets:
			default:
				return 7 + level * 3;
		}
	}
}