A Perk class for an enemy-explosion ability called "Combustion". It tracks kill counts, enables a temporary combustion state when the required number of kills is reached, computes explosion radius and damage from perk level and player stats, and updates UI display values and highlight visuals.
using System;
using Sandbox;
[Perk( Rarity.Epic, locked: true, minUnlocksReq: 1, alwaysOfferDebug: false, IncludedCategories = new[] { PerkCategory.Aoe, PerkCategory.Explosion })]
public class PerkEnemyExplode : Perk
{
private enum Mod { NumKillsReq, HpPercent };
private int _killCounter;
private const float EXPLOSION_RADIUS = 80f;
static PerkEnemyExplode()
{
Register<PerkEnemyExplode>(
name: "Combustion",
imagePath: "textures/icons/vector/enemy_explode.png",
description: level => $"Every {(int)GetValue( level, Mod.NumKillsReq )}th enemy you kill explodes for {(int)GetValue( level, Mod.HpPercent, true )}% of their max hp as dmg",
upgradeDescription: level => $"Every [+]{(int)GetValue( level - 1, Mod.NumKillsReq )}th→{(int)GetValue( level, Mod.NumKillsReq )}th[/+] enemy you kill explodes for {(int)GetValue( level - 1, Mod.HpPercent, true )}%→{(int)GetValue( level, Mod.HpPercent, true )}% of their max hp as dmg"
);
}
public override void Start()
{
base.Start();
DisplayCooldownColor = new Color( 1f, 0.2f, 0f, 3f );
HighlightColor = new Color( 1f, 0.75f, 0.75f );
HighlightDuration = 0.25f;
HighlightOpacity = 3f;
}
public override void Refresh()
{
base.Refresh();
int numReq = (int)GetValue( Level, Mod.NumKillsReq );
DisplayText = Player.CombustionActive ? $"✔️" : $"{Math.Max( numReq - _killCounter, 1 ) }";
DisplayCooldown = Player.CombustionActive ? 1f : Utils.Map( Math.Min( _killCounter, numReq - 1 ), 0, numReq, 0f, 1f );
}
public override void Update( float dt )
{
base.Update( dt );
if ( !Player.CombustionActive )
{
int numReq = (int)GetValue( Level, Mod.NumKillsReq );
DisplayText = $"{numReq - _killCounter}";
DisplayCooldown = Utils.Map( _killCounter, 0, numReq, 0f, 1f );
DisplayCooldownColor = new Color( 1f, 0.2f, 0f, 3f );
ShouldUpdate = false;
}
else
{
DisplayCooldownColor = new Color( 1f, 0.2f, 0.0f, Utils.Map( Utils.FastSin( RealTime.Now * 32f ), -1f, 1f, 0.2f, 2f ) );
}
}
private static float GetValue( int level, Mod mod, bool isPercent = false )
{
switch ( mod )
{
case Mod.NumKillsReq:
default:
return 11 - level;
case Mod.HpPercent:
return isPercent
? 10f + 25f * level
: 0.10f + 0.25f * level;
}
}
public override void OnKill( Enemy enemy, DamageType damageType, bool countsAsKill )
{
base.OnKill( enemy, damageType, countsAsKill );
if ( !countsAsKill ) return;
//if ( damageType == DamageType.Explosion )
// return;
_killCounter++;
int numReq = (int)GetValue( Level, Mod.NumKillsReq );
if ( _killCounter >= numReq )
{
float radius = EXPLOSION_RADIUS * Player.Stats[PlayerStat.RadiusMultiplier] * Player.Stats[PlayerStat.ExplosionSizeMultiplier];
float damageFactor = GetValue( Level, Mod.HpPercent ) * Player.Stats[PlayerStat.ExplosionDamageMultiplier];
Player.EnableCombustion( radius, damageFactor );
// todo: if get +radius perk, update active combustion radius
ShouldUpdate = true;
_killCounter = 0;
Highlight();
}
DisplayText = Player.CombustionActive ? $"✔️" : $"{numReq - _killCounter}";
DisplayCooldown = Player.CombustionActive ? 1f : Utils.Map( _killCounter, 0, numReq, 0f, 1f );
}
public override void Remove( bool restart = false )
{
base.Remove( restart );
if( Player.CombustionActive )
Player.DisableCombustion();
}
}