perks/PerkTeleportToPlayer.cs

A game perk class 'PerkTeleportToPlayer' that teleports the player to the farthest other player and heals them when their HP drops below a threshold. It manages cooldown, UI display values, level-based modifiers (cooldown, hp threshold, heal amount), and responds to OnHit to trigger the warp.

NetworkingFile Access
using System;
using Sandbox;

[Perk( Rarity.Mythic, multiplayerMode: MultiplayerMode.OnlyMultiplayer, locked: true, minUnlocksReq: 2, alwaysOfferDebug: false )]
public class PerkTeleportToPlayer : Perk
{
	private enum Mod { HpThreshold, Cooldown, HealAmount };

	private TimeSince _timeSinceTeleporting;
	private bool _isActive;

	static PerkTeleportToPlayer()
	{
		Register<PerkTeleportToPlayer>(
			name: "Emergency Warp",
			imagePath: "textures/icons/vector/teleport_to_player.png",
			description: level => $"When hit below {GetValue( level, Mod.HpThreshold, true )}% hp,\nteleport to the farthest player\nand heal {GetValue( level, Mod.HealAmount )} hp\n(cooldown: {GetValue( level, Mod.Cooldown )}s)",
			upgradeDescription: level => $"When hit below {GetValue( level, Mod.HpThreshold, true )}% hp,\nteleport to the farthest player\nand heal {GetValue( level - 1, Mod.HealAmount )}→{GetValue( level, Mod.HealAmount )} hp\n(cooldown: {GetValue( level - 1, Mod.Cooldown )}→{GetValue( level, Mod.Cooldown )}s)",
			descriptionLineHeight: 9.5f
		);
	}

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

		ShouldUpdate = true;
		_timeSinceTeleporting = 60f;

		DisplayCooldownColor = new Color( 0.7f, 0.7f, 1f, 2f );
	}

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

		_isActive = true;
	}

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

	}

	public override void Update( float dt )
	{
		base.Update( dt );

		float cooldown = GetValue( Level, Mod.Cooldown );

		DisplayText = _timeSinceTeleporting < cooldown ? $"{MathX.CeilToInt( cooldown - _timeSinceTeleporting )}" : " ";
		DisplayCooldown = Utils.Map( _timeSinceTeleporting, 0f, cooldown, 1f, 0f );

		if ( _timeSinceTeleporting > cooldown )
		{
			_isActive = true;
			ShouldUpdate = false;
			DisplayText = " ";
			DisplayCooldown = 0f;

			HighlightColor = new Color( 0.7f, 0.7f, 1f );
			HighlightDuration = 0.3f;
			HighlightOpacity = 3f;
			Highlight();
		}
	}

	private static float GetValue( int level, Mod mod, bool isPercent = false )
	{
		switch ( mod )
		{
			case Mod.Cooldown:
			default:
				return 40f - level * 10f;
			case Mod.HpThreshold:
				return isPercent ? 20f : 0.20f;
			case Mod.HealAmount:
				return level * 10f;
		}
	}

	void WarpTo( Vector2 pos )
	{
		Player.Teleport( pos );

		_timeSinceTeleporting = 0f;
		_isActive = false;
		ShouldUpdate = true;

		float cooldown = GetValue( Level, Mod.Cooldown );
		DisplayText = $"{MathX.CeilToInt( cooldown )}";

		Player.Heal( GetValue( Level, Mod.HealAmount ) );
	}

	public override void OnHit( float amount, DamageType damageType, bool isSelfInflicted, Vector2 dir, float force, Enemy enemySource, EnemyType enemyType, float previousHealth )
	{
		base.OnHit( amount, damageType, isSelfInflicted, dir, force, enemySource, enemyType, previousHealth );

		if( damageType == DamageType.Self )
			return;

		if ( !_isActive )
			return;

		float hpPercent = Player.Health / Player.Stats[PlayerStat.MaxHp];
		if ( hpPercent > GetValue( Level, Mod.HpThreshold ) )
			return;

		Player farthestPlayer = null;
		float farthestDistSqr = 0f;

		foreach ( Player player in Manager.Instance.Players )
		{
			if ( player == Player )
				continue;

			float distSqr = (player.Position2D - Player.Position2D).LengthSquared;
			if ( distSqr > farthestDistSqr )
			{
				farthestPlayer = player;
				farthestDistSqr = distSqr;
			}
		}

		if ( farthestPlayer.IsValid() )
		{
			WarpTo( farthestPlayer.Position2D + Utils.GetRandomVector() * 40f );

			HighlightColor = new Color( 0.5f, 0.5f, 1f );
			HighlightDuration = 0.5f;
			HighlightOpacity = 4f;
			Highlight();
		}
	}
}