perks/PerkHealthPackHealAllies.cs

A multiplayer-only Epic perk that modifies healthpack behavior. It increases the player's extra HP from healthpacks and when the player gains a healthpack it spawns a visual ring and either heals nearby allies or damages them depending on a stat, using RPC calls to apply HealRpc/DamageRpc to nearby players.

NetworkingFile Access
using Sandbox;
using System;

[Perk( Rarity.Epic, multiplayerMode: MultiplayerMode.OnlyMultiplayer, locked: true, alwaysOfferDebug: false, IncludedCategories = new[] { PerkCategory.Aoe })]
public class PerkHealthPackHealAllies : Perk
{
	private enum Mod { Percent };

	public const float HEAL_RADIUS = 120f;


	static PerkHealthPackHealAllies()
	{
		Register<PerkHealthPackHealAllies>(
			name: "Shared Needles",
			imagePath: "textures/icons/vector/health_pack_heal_allies.png",
			description: level => $"Nearby players get\n+{(int)GetValue( level, Mod.Percent, true )}% hp from healthpacks you get",
			upgradeDescription: level => $"Nearby players get\n+{(int)GetValue( level - 1, Mod.Percent, true )}%→+{(int)GetValue( level, Mod.Percent, true )}% hp\nfrom healthpacks you get"
		);
	}

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

		HighlightColor = new Color( 0f, 1f, 0f );
		HighlightDuration = 0.75f;
		HighlightOpacity = 2f;
	}

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

		Player.Modify( this, PlayerStat.HealthPackExtraHp, GetValue( Level, Mod.Percent ), ModifierType.Add );
	}

	public override void OnGainHealthpack( float amount )
	{
		base.OnGainHealthpack( amount );

		Vector2 pos = Player.Position2D;
		float radius = HEAL_RADIUS * Player.Stats[PlayerStat.RadiusMultiplier];

		//if ( Player.Stats[PlayerStat.HealthpackXpPercent] > 0f )
		//{
		//	Manager.Instance.SpawnRingRpc( Player.Position2D, radius, new Color( 0.3f, 0.35f, 1f, 0.5f ), lifetime: 0.45f, path: "ring2" );

		//	var xpAmount = amount * Player.Stats[PlayerStat.HealthpackXpPercent];
		//	foreach ( Player player in Manager.Instance.AlivePlayers )
		//	{
		//		if ( player == Player )
		//			continue;

		//		if ( (player.Position2D - pos).LengthSquared < MathF.Pow( radius, 2f ) )
		//			player.AddXpRpc( xpAmount, XpSource.Healthpack );
		//	}

		//	HighlightColor = new Color( 0.3f, 0.3f, 1f );
		//	Highlight();
		//}
		
		if( Player.Stats[PlayerStat.HealthPacksHurt] > 0f )
		{
			Manager.Instance.SpawnRingRpc( Player.Position2D, radius, new Color(1f, 0.25f, 0.3f, 0.6f ), lifetime: 0.45f, path: "ring_spiky_2" );

			var hurtAmount = amount * GetValue( Level, Mod.Percent );
			foreach ( Player player in Manager.Instance.AlivePlayers )
			{
				if ( player == Player )
					continue;

				if ( (player.Position2D - pos).LengthSquared < MathF.Pow( radius, 2f ) )
				{
					var dir = (player.Position2D - Player.Position2D).Normal;
					var hitPos = player.Position2D - dir * player.Radius;
					player.DamageRpc( hurtAmount, DamageType.Aoe, hitPos, dir, upwardAmount: 0f, force: hurtAmount * 5f, ragdollForce: hurtAmount * 0.1f, enemySource: null, enemyType: EnemyType.None );
				}
			}

			HighlightColor = new Color( 1f, 0f, 0f );
			Highlight();
		}
		else
		{
			Manager.Instance.SpawnRingRpc( Player.Position2D, radius, new Color( 0.3f, 1f, 0.35f, 0.5f ), lifetime: 0.45f, path: "ring2" );

			var healAmount = amount * GetValue( Level, Mod.Percent );
			foreach ( Player player in Manager.Instance.AlivePlayers )
			{
				if ( player == Player )
					continue;

				if ( (player.Position2D - pos).LengthSquared < MathF.Pow( radius, 2f ) && player.Health < player.GetSyncStat( PlayerStat.MaxHp ) )
					player.HealRpc( healAmount, playSfx: true, Player );
			}

			HighlightColor = new Color( 0f, 1f, 0f );
			Highlight();
		}

		//var healthpackMaxHpLevel = Player.GetPerkLevel( TypeLibrary.GetType( typeof( PerkHealthPackMaxHp ) ) );
		//if ( healthpackMaxHpLevel > 0 && !(Player.Health < Player.Stats[PlayerStat.MaxHp]) )
		//{

		//}
	}

	private static float GetValue( int level, Mod mod, bool isPercent = false )
	{
		switch ( mod )
		{
			case Mod.Percent:
			default:
				return isPercent
					? 40f + 40f * level
					: 0.40f + 0.40f * level;
		}
	}
}