A rare perk class 'PerkDodgeHpDamage' that, when active, causes an attacker to take damage equal to a percent of their remaining HP when their melee attack is dodged. It manages cooldown, UI display colors, highlight effects, and applies damage via enemySource.DamageRpc on OnDodged.
using Sandbox;
using System;
using System.Numerics;
[Perk( Rarity.Rare, includedAtStart: false, locked: true, minUnlocksReq: 1, alwaysOfferDebug: false )]
public class PerkDodgeHpDamage : Perk
{
private enum Mod { DmgHpPercent, Cooldown };
private TimeSince _timeSinceDmg;
private bool _isActive;
static PerkDodgeHpDamage()
{
Register<PerkDodgeHpDamage>(
name: "Jujitsu",
imagePath: "textures/icons/vector/dodge_hp_damage.png",
description: level => $"When you dodge melee dmg, hurt the attacker for {(int)GetValue( level, Mod.DmgHpPercent, true )}% of their remaining hp __ (cooldown: {GetValue( level, Mod.Cooldown )}s)",
upgradeDescription: level => $"When you dodge melee dmg, hurt the attacker for {(int)GetValue( level - 1, Mod.DmgHpPercent, true )}%→{(int)GetValue( level, Mod.DmgHpPercent, true )}% of their remaining hp __ (cooldown: {GetValue( level - 1, Mod.Cooldown )}→{GetValue( level, Mod.Cooldown )}s)"
);
}
public override void Start()
{
base.Start();
DisplayCooldownColor = new Color( 0.7f, 0.7f, 1f, 0.5f );
HighlightColor = new Color( 0.3f, 0.4f, 1f );
HighlightDuration = 0.3f;
HighlightOpacity = 4f;
}
public override void IncreaseLevel()
{
base.IncreaseLevel();
_isActive = true;
ShouldUpdate = false;
DisplayCooldown = 0f;
}
public override void Refresh()
{
base.Refresh();
}
public override void Update( float dt )
{
base.Update( dt );
var cooldown = GetValue( Level, Mod.Cooldown );
if ( _timeSinceDmg > cooldown )
{
_isActive = true;
DisplayCooldown = 0f;
ShouldUpdate = false;
}
else
{
DisplayCooldown = Utils.Map( _timeSinceDmg, 0f, cooldown, 1f, 0f );
}
}
private static float GetValue( int level, Mod mod, bool isPercent = false )
{
switch ( mod )
{
case Mod.DmgHpPercent:
default:
return isPercent
? 5 + 3 * level
: 0.05f + 0.03f * level;
case Mod.Cooldown:
return 5f - level * 1f;
}
}
public override void OnDodged( float damage, DamageType damageType, Vector2 dir, Enemy enemySource )
{
base.OnDodged( damage, damageType, dir, enemySource );
if ( !_isActive )
return;
if ( enemySource == null )
return;
if ( !Player.IsDamageTypeMelee( damageType ) )
return;
Vector2 pos = Player.Position2D;
float dmgHpPercent = GetValue( Level, Mod.DmgHpPercent );
float dmg = enemySource.Health * dmgHpPercent;
var hitPos = enemySource.WorldPosition + (Vector3)dir * enemySource.Radius;
enemySource.DamageRpc( dmg, Player, DamageType.Melee, hitPos, force: dir * dmg * 2f, isCrit: false, shouldFlinch: true, damageFlags: DamageResultFlags.DodgeHpDamage );
// todo: sfx
_timeSinceDmg = 0f;
Highlight();
_isActive = false;
ShouldUpdate = true;
DisplayCooldown = 1f;
}
}