A Perk component named CurseSpawnZombies that periodically spawns a temporary zombie near the player. It tracks time since last spawn, triggers a spawn via Manager.Instance.SpawnEnemyRpc when the interval elapses, and updates UI display properties like icon and cooldown.
using System;
using Sandbox;
[Perk( Rarity.Unique, curse: true, alwaysOfferDebug: false )]
public class CurseSpawnZombies : Perk
{
private enum Mod { Time };
private TimeSince _timeSinceZombie;
static CurseSpawnZombies()
{
Register<CurseSpawnZombies>(
name: "Unmarked Graves",
imagePath: "textures/icons/vector/curse_spawn_zombie.png",
description: level => $"Spawn a temporary Zombie\nnearby every [-]{GetValue( level, Mod.Time ).ToString( "0.#" )}s[/-]"
);
}
public override void Start()
{
base.Start();
ShouldUpdate = true;
HighlightColor = new Color( 0.5f, 0f, 0f );
HighlightDuration = 0.75f;
HighlightOpacity = 3.5f;
DisplayCooldownColor = new Color( 0.75f, 0.2f, 0.25f );
_timeSinceZombie = 0f;
}
public override void Refresh()
{
base.Refresh();
}
public override void Update( float dt )
{
base.Update( dt );
var timeReq = GetValue( Level, Mod.Time );
if(_timeSinceZombie > timeReq)
{
var dir = Player.MoveVector.LengthSquared > 0f
? Player.MoveVector
: Utils.GetRandomVector();
Manager.Instance.SpawnEnemyRpc(
EnemyType.ZombieTemporary,
Player.Position2D + Utils.GetRandomVectorInCone( dir, 60f ) * Player.Radius + Player.Velocity * Game.Random.Float( 1f, 2.5f )
);
_timeSinceZombie = 0f;
Highlight();
IconScale = Game.Random.Float( 1.1f, 1.2f );
IconAngleOffset = Game.Random.Float( 5f, 8f ) * (Game.Random.Int( 0, 1 ) == 0 ? -1f : 1f);
}
DisplayText = Level > 1
? " "
: $"{MathX.CeilToInt( timeReq - _timeSinceZombie )}";
DisplayCooldown = Utils.Map( _timeSinceZombie, 0f, timeReq, 0f, 1f );
}
private static float GetValue( int level, Mod mod, bool isPercent = false )
{
switch ( mod )
{
case Mod.Time:
default:
return 3f;
}
}
}