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.
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();
}
}
}