A multiplayer-only perk named "Sacrificial Pulse" that periodically damages the owner and heals nearby other players. It checks nearby alive players inside a radius, heals them via HealRpc, spawns a visual ring, plays a sound, and applies self-damage to the perk owner on a 1s interval.
using Sandbox;
using System;
[Perk( Rarity.Rare, multiplayerMode: MultiplayerMode.OnlyMultiplayer, locked: true, minUnlocksReq: 2, alwaysOfferDebug: false, IncludedCategories = new[] { PerkCategory.SelfDmg, PerkCategory.Aoe })]
public class PerkDrainHealOtherPlayers : Perk
{
private enum Mod { DrainAmount, HealAmount };
public const float HEAL_RADIUS = 150f;
private TimeSince _timeSinceHeal;
static PerkDrainHealOtherPlayers()
{
Register<PerkDrainHealOtherPlayers>(
name: "Sacrificial Pulse",
imagePath: "textures/icons/vector/drain_heal_other_players.png",
description: level => $"When near other players,\nget -{GetValue( level, Mod.DrainAmount ).ToString("0.#")} hp/s\nand heal them for [+]{GetValue( level, Mod.HealAmount ).ToString("0.#")}[/+] hp/s",
upgradeDescription: level => $"When near other players,\nget -{GetValue( level - 1, Mod.DrainAmount ).ToString( "0.#" )}→-{GetValue( level, Mod.DrainAmount ).ToString("0.#")} hp/s\nand heal them for {GetValue( level - 1, Mod.HealAmount ).ToString( "0.#" )}→{GetValue( level, Mod.HealAmount ).ToString("0.#")} hp/s"
);
}
public override void Start()
{
base.Start();
HighlightColor = new Color( 0.8f, 1f, 0.8f );
HighlightDuration = 0.4f;
HighlightOpacity = 1.5f;
ShouldUpdate = true;
}
public override void Refresh()
{
base.Refresh();
_timeSinceHeal = 0f;
}
public override void Update( float dt )
{
base.Update( dt );
if ( _timeSinceHeal > 1f )
{
Vector2 pos = Player.Position2D;
float radius = HEAL_RADIUS * Player.Stats[PlayerStat.RadiusMultiplier];
bool didHeal = false;
foreach ( Player player in Manager.Instance.AlivePlayers )
{
if ( player == Player )
continue;
if ( (player.Position2D - pos).LengthSquared < MathF.Pow( radius, 2f ) )
{
didHeal = true;
if ( player.Health < player.GetSyncStat( PlayerStat.MaxHp ) )
player.HealRpc( GetValue( Level, Mod.HealAmount ), playSfx: false, Player );
}
}
if ( didHeal )
{
Manager.Instance.SpawnRingRpc( Player.Position2D, radius, new Color( 0.8f, 0.5f, 0.8f, 0.1f ), lifetime: 0.5f, path: "ring2" );
Player.Damage( GetValue( Level, Mod.DrainAmount ), DamageType.Self, Player.Position2D, Utils.GetRandomVector(), upwardAmount: 0f, force: 0f, ragdollForce: 1f, enemySource: null, enemyType: EnemyType.None );
Manager.Instance.PlaySfxNearbyRpc( "heal", Player.Position2D, pitch: Utils.Map( Player.HpPercent, 1f, 0f, 1.5f, 2f ), volume: 0.4f, maxDist: 240f );
Highlight();
IconScale = Game.Random.Float( 1.2f, 1.3f );
IconAngleOffset = Game.Random.Float( 10f, 20f ) * (Game.Random.Int( 0, 1 ) == 0 ? -1f : 1f);
}
_timeSinceHeal = 0f;
}
}
private static float GetValue( int level, Mod mod, bool isPercent = false )
{
switch ( mod )
{
case Mod.DrainAmount: default:
return 1f + 1f * level;
case Mod.HealAmount:
return 1.2f + 1.1f * level;
}
}
}