Perk component 'PerkMoveToSpawnFire'. It tracks player movement distance and when the player moves a threshold distance it spawns a ground fire, plays a nearby SFX, triggers a dodge/duck RPC on the player, and updates perk UI state (highlight, icon scale/angle, cooldown).
using System;
using Sandbox;
[Perk( Rarity.Legendary, locked: true, alwaysOfferDebug: false, IncludedCategories = new[] { PerkCategory.Aoe, PerkCategory.Fire })]
public class PerkMoveToSpawnFire : Perk
{
private enum Mod { RequiredDist };
private float _currDist;
static PerkMoveToSpawnFire()
{
Register<PerkMoveToSpawnFire>(
name: "Firewalker",
imagePath: "textures/icons/vector/move_to_spawn_fire.png",
description: level => $"Start a fire when you move {GetValue( level, Mod.RequiredDist, true ).ToString( "0.#" )}m",
upgradeDescription: level => $"Start a fire when\nyou move {GetValue( level - 1, Mod.RequiredDist, true ).ToString("0.#")}→{GetValue( level, Mod.RequiredDist, true ).ToString("0.#")}m"
);
}
public override void Start()
{
base.Start();
ShouldUpdate = true;
DisplayCooldownColor = new Color( 1f, 0.5f, 1f, 0.5f );
HighlightColor = new Color( 0.8f, 0.8f, 1f );
HighlightDuration = 0.2f;
HighlightOpacity = 2f;
}
public override void Refresh()
{
base.Refresh();
}
private static float GetValue( int level, Mod mod, bool isPercent = false )
{
switch ( mod )
{
case Mod.RequiredDist:
default:
return (7.5f - 2.5f * level) * (isPercent ? 1f : Utils.Meter2Unit);
}
}
public override void Update( float dt )
{
base.Update( dt );
//DisplayText = $"{string.Format("{0:0.0}", GetValue(Level, Mod.RequiredDist) - _currDist)}";
if ( Player.TotalVelocity.LengthSquared > 0f )
{
_currDist += Player.TotalVelocity.Length * dt;
if ( _currDist > GetValue( Level, Mod.RequiredDist ) )
{
var dir = Player.TotalVelocity.Normal;
var pos = Player.Position2D - dir * 20f;
Manager.Instance.SpawnFireGroundRpc(
pos,
Player,
enemySource: null,
enemyType: EnemyType.None,
damage: Player.Stats[PlayerStat.FireDamage],
lifetime: Player.Stats[PlayerStat.FireLifetime],
spreadChance: Player.Stats[PlayerStat.FireSpreadChance],
canStack: Player.Stats[PlayerStat.FireDmgStack] > 0f,
scale: Player.Stats[PlayerStat.RadiusMultiplier],
colorA: Color.Red,
colorB: Color.Yellow
);
Manager.Instance.PlaySfxNearbyRpc( "burn", Player.Position2D, pitch: Game.Random.Float( 0.95f, 1f ), volume: 0.9f, maxDist: 300f );
_currDist = 0f;
DisplayCooldown = 0f;
Highlight();
IconScale = Game.Random.Float( 1.2f, 1.3f );
IconAngleOffset = Game.Random.Float( 10f, 20f ) * (Game.Random.Int( 0, 1 ) == 0 ? -1f : 1f);
Player.DodgeDuckRpc( dir, time: Game.Random.Float( 0.15f, 0.25f ) );
}
else
{
DisplayCooldown = Utils.Map( _currDist, 0f, GetValue( Level, Mod.RequiredDist ), 0f, 1f );
}
}
}
}