A unique curse perk that periodically spawns a 'Spiker head' near the player. It tracks a cooldown, visual crack marker, and triggers RPCs on the Manager to show cracks and then spawn the spiker head shortly after.
using System;
using Sandbox;
[Perk( Rarity.Unique, curse: true, alwaysOfferDebug: false )]
public class CurseSpawnSpikerHead : Perk
{
private enum Mod { Time };
private TimeSince _timeSinceSpawn;
private Vector2 _cracksPos;
private TimeSince _timeSinceCracks;
private bool _isShooting;
static CurseSpawnSpikerHead()
{
Register<CurseSpawnSpikerHead>(
name: "Nemesis Spikes",
imagePath: "textures/icons/vector/curse_spawn_spiker_head.png",
description: level => $"Spawn a Spiker head\nnearby every [-]{(int)GetValue( level, Mod.Time )}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 );
_timeSinceSpawn = 0f;
}
public override void Refresh()
{
base.Refresh();
}
public override void Update( float dt )
{
base.Update( dt );
var timeReq = GetValue( Level, Mod.Time );
if( _isShooting && _timeSinceCracks > 0.7f )
{
Manager.Instance.SpawnSpikerHeadRpc(
_cracksPos
);
_isShooting = false;
}
if(_timeSinceSpawn > timeReq)
{
var dir = Player.MoveVector.LengthSquared > 0f
? Player.MoveVector
: Utils.GetRandomVector();
_cracksPos = Manager.Instance.ClampPosToBounds( Player.Position2D + Utils.GetRandomVectorInCone( dir, 60f ) * Player.Radius + Player.Velocity * Game.Random.Float( 0.3f, 3f ) );
Manager.Instance.SpawnSpikerHeadCracksRpc( _cracksPos, color: new Color( 0.09f, 0.02f, 0.01f ) );
_isShooting = true;
_timeSinceCracks = 0f;
_timeSinceSpawn = 0f;
Highlight();
IconScale = Game.Random.Float( 1.1f, 1.2f );
IconAngleOffset = Game.Random.Float( 5f, 10f ) * (Game.Random.Int( 0, 1 ) == 0 ? -1f : 1f);
}
DisplayText = Level > 1
? " "
: $"{MathX.CeilToInt( timeReq - _timeSinceSpawn )}";
DisplayCooldown = Utils.Map( _timeSinceSpawn, 0f, timeReq, 0f, 1f );
}
private static float GetValue( int level, Mod mod, bool isPercent = false )
{
switch ( mod )
{
case Mod.Time:
default:
return 5f;
//switch( level )
//{
// case 1: default: return 7f;
// case 2: return 3f;
//}
}
}
}