A unique curse perk called "Bloody Feet" that drains the players health when they move. It accumulates distance traveled and when a distance threshold is passed it applies self-damage equal to a configured amount, then resets distance and damage counters and triggers UI highlights.
using System;
using Sandbox;
[Perk( Rarity.Unique, curse: true, alwaysOfferDebug: false, IncludedCategories = new[] { PerkCategory.SelfDmg })]
public class CurseMoveDrainHealth : Perk
{
private enum Mod { Distance, MaxHpLoss };
private float _currDamage;
private float _currDist;
static CurseMoveDrainHealth()
{
Register<CurseMoveDrainHealth>(
name: "Bloody Feet",
imagePath: "textures/icons/vector/curse_move_drain_health.png",
description: level => $"-{(int)GetValue( level, Mod.MaxHpLoss )} hp when you move [-]{GetValue( level, Mod.Distance, true ).ToString( "0.#" )}[/-]m"
);
}
public override void Start()
{
base.Start();
ShouldUpdate = true;
DisplayCooldownColor = new Color( 1f, 0f, 0f, 0.5f );
HighlightColor = new Color( 1f, 0f, 0f );
HighlightDuration = 0.25f;
HighlightOpacity = 3f;
}
public override void Refresh()
{
base.Refresh();
}
public override void Update( float dt )
{
base.Update( dt );
//bool isCurrentlyDraining = false;
if ( Player.TotalVelocity.LengthSquared > 0.1f )
{
float vel = Player.TotalVelocity.Length;
_currDist += vel * dt;
var distReq = GetValue( Level, Mod.Distance );
if ( _currDist > distReq )
{
if ( !(Player.IsInvincible && Player.Stats[PlayerStat.IgnoreSelfDamageWhenInvuln] > 0f) )
{
_currDamage += GetValue( Level, Mod.MaxHpLoss );
if ( _currDamage > 0.85f )
{
Player.Damage( _currDamage, DamageType.Self, Player.Position2D, Utils.GetRandomVector(), upwardAmount: 0f, force: 0f, ragdollForce: 0f, enemySource: null, enemyType: EnemyType.None );
float percent = Utils.Map( Player.Health, Player.Stats[PlayerStat.MaxHp], 0f, 0f, 1f );
//Manager.Instance.PlaySfxNearbyRpc( "zombie.attack.player", Player.Position2D, pitch: Utils.Map( percent, 0f, 1f, 1.4f, 1.75f, EasingType.QuadIn ), volume: Utils.Map( percent, 0f, 1f, 0.25f, 0.5f, EasingType.QuadIn ), maxDist: 180f );
_currDamage = 0f;
Highlight();
IconScale = Game.Random.Float( 1.1f, 1.2f );
IconAngleOffset = Game.Random.Float( 10f, 15f ) * (Game.Random.Int( 0, 1 ) == 0 ? -1f : 1f);
}
}
_currDist -= distReq;
DisplayCooldown = 0f;
}
else
{
DisplayCooldown = Utils.Map( _currDist, 0f, distReq, 0f, 1f );
}
//Player.Modify( this, PlayerStat.HpRegenDisplay, -GetValue( Level, Mod.MaxHpLoss ) * vel * 0.02f, ModifierType.Add );
//isCurrentlyDraining = true;
}
//if ( !isCurrentlyDraining || Player.TotalVelocity.LengthSquared < 0.25f )
// Player.StopModifying( this, PlayerStat.HpRegenDisplay );
}
private static float GetValue( int level, Mod mod, bool isPercent = false )
{
switch ( mod )
{
case Mod.Distance:
default:
return 2.8f * (isPercent ? 1f : Utils.Meter2Unit);
case Mod.MaxHpLoss:
return 2;
}
}
}