perks/PerkHealPlayerShoot.cs

Perk class that triggers when this player heals another player. It computes a damage value based on the heal amount and perk level, spawns a bullet toward the nearest enemy (or random direction), and shows a brief highlight and scale visual effect.

Networking
using System;
using Sandbox;

[Perk( Rarity.Rare, multiplayerMode: MultiplayerMode.OnlyMultiplayer, alwaysOfferDebug: false )]
public class PerkHealPlayerShoot : Perk
{
	private enum Mod { DamagePercent };

	// todo: too op with PerkBulletHealTeammate?

	// todo: only offer once you can do some healing?

	static PerkHealPlayerShoot()
	{
		Register<PerkHealPlayerShoot>(
			name: "Protector Shots",
			imagePath: "textures/icons/vector/heal_player_shoot.png",
			description: level => $"When you heal another player, launch a bullet-icon with\n[+]{GetValue( level, Mod.DamagePercent, true )}%[/+] of the hp as dmg",
			upgradeDescription: level => $"When you heal another player, launch a bullet-icon with\n{GetValue( level - 1, Mod.DamagePercent, true )}%→{GetValue( level, Mod.DamagePercent, true )}% of the hp as dmg"
		);
	}

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

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

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

	}

	private static float GetValue( int level, Mod mod, bool isPercent = false )
	{
		switch ( mod )
		{
			case Mod.DamagePercent:
			default:
				return isPercent
					? 150f + 100f * level
					: 1.5f + 1.00f * level;
		}
	}

	public override void OnHealOther( float amount, Player other )
	{
		base.OnHealOther( amount, other );

		Shoot( amount );
	}

	void Shoot( float healAmount )
	{
		var closestEnemy = Manager.Instance.GetClosestEnemy( Player.Position2D, 400f );
		Vector2 dir = closestEnemy != null ? (closestEnemy.Position2D - Player.Position2D).Normal : Utils.GetRandomVector();

		var pos = Player.Position2D + dir * Player.BULLET_SPAWN_OFFSET * Player.Stats[PlayerStat.Scale];
		var damage = healAmount * GetValue( Level, Mod.DamagePercent );
		damage *= Player.Stats[PlayerStat.BulletDamageMultiplier];
		damage = MathF.Max( damage, Player.MIN_BULLET_DAMAGE );

		Player.SpawnBullet( pos, dir, damage );
		//bullet.Stats[BulletStat.HealTeammateAmount] = 0f;

		Highlight();

		Player.ScaleHeightRpc( amount: Utils.Map( damage, 1f, 20f, 1.25f, 1.5f ), time: Game.Random.Float( 0.05f, 0.06f ) );
	}
}