A Perk component called PerkHurtUniqueChoice (Spirit Quest). It tracks damage taken and every configured amount of damage awards the player a pending Unique perk choice, updates icon/cooldown display, and triggers highlight visuals.
using System;
using Sandbox;
[Perk( Rarity.Unique, locked: true, minUnlocksReq: 2, alwaysOfferDebug: false )]
public class PerkHurtUniqueChoice : Perk
{
private enum Mod { ReqDmg };
private float _dmgTaken;
static PerkHurtUniqueChoice()
{
Register<PerkHurtUniqueChoice>(
name: "Spirit Quest",
imagePath: "textures/icons/vector/hurt_unique_choice.png",
description: level => $"Every {(int)GetValue( level, Mod.ReqDmg )} dmg taken,\nsee a Unique perk choice",
upgradeDescription: level => $"Every {(int)GetValue( level - 1, Mod.ReqDmg )}→{(int)GetValue( level, Mod.ReqDmg )} dmg taken,\nsee a Unique perk choice"
);
}
public override void Start()
{
base.Start();
DisplayCooldownColor = new Color( 1f, 1f, 0.3f, 0.5f );
}
public override void Refresh()
{
base.Refresh();
CheckDmg();
RefreshIcon();
}
void CheckDmg()
{
var dmgReq = GetValue( Level, Mod.ReqDmg );
if ( _dmgTaken >= dmgReq )
{
_dmgTaken -= dmgReq;
Player.NumPendingUniqueChoices++;
HighlightColor = new Color( 1f, 1f, 0.3f );
HighlightDuration = 0.5f;
HighlightOpacity = 4f;
Highlight();
// todo: sfx
}
}
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 );
_dmgTaken += amount;
CheckDmg();
RefreshIcon();
}
public override void OnPerkChoicesRefreshed( int numUniqueConsumed )
{
base.OnPerkChoicesRefreshed( numUniqueConsumed );
HighlightColor = new Color( 1f, 1f, 0.3f );
HighlightDuration = 0.75f;
HighlightOpacity = 4f;
Highlight();
RefreshIcon();
}
void RefreshIcon()
{
var pending = Player.NumPendingUniqueChoices;
DisplayText = pending > 0 ? $"+{pending}" : "";
DisplayCooldown = Utils.Map( _dmgTaken, 0f, GetValue( Level, Mod.ReqDmg ), 0f, 1f );
}
private static float GetValue( int level, Mod mod, bool isPercent = false )
{
switch ( mod )
{
case Mod.ReqDmg:
default:
return 150f;
}
}
}