A legendary perk component that implements a "Fear Gaze" ability for a player. It spawns particle effects, traces a forward cone to detect enemy units, accumulates gaze time on a targeted unit and applies a fear status when the gaze duration exceeds a threshold; it also updates particle visuals while gazing.
using System;
using Sandbox;
[Perk( Rarity.Legendary, disabled: true, alwaysOfferDebug: false, IncludedCategories = new[] { PerkCategory.Fear })]
public class PerkFearGaze : Perk
{
private enum Mod { Time };
private float _tickTimer;
private const float TICK_TIME = 0.15f;
private Unit _targetUnit;
private float _gazeTime;
private GameObject _particles;
private ParticleEffect _particleRing;
private ParticleRingEmitter _particleRingEmitter;
private const float GAZE_RANGE = 350f;
static PerkFearGaze()
{
//Register<PerkFearGaze>(
// name: "Fearsome Gaze",
// imagePath: "textures/icons/vector/fear_gaze.png",
// description: level => $"Staring at enemies for {GetValue( level, Mod.Time ).ToString( "0.#" )}s\nscares them",
// upgradeDescription: level => $"Staring at enemies for {GetValue( level - 1, Mod.Time ).ToString("0.#")}→{GetValue( level, Mod.Time ).ToString("0.#")}s\nscares them"
//);
}
public override void Start()
{
base.Start();
HighlightColor = new Color( 0.5f, 0f, 1f );
HighlightDuration = 0.25f;
HighlightOpacity = 3f;
_particles = GameObject.Clone( "prefabs/effects/fear_gaze_particles.prefab", new global::Transform( Player.WorldPosition.WithZ( -100f ) ) );
_particleRing = _particles.GetComponent<ParticleEffect>();
_particleRing.Alpha = 0f;
_particleRingEmitter = _particles.GetComponent<ParticleRingEmitter>();
_particleRingEmitter.Rate = 0;
ShouldUpdate = true;
}
public override void Refresh()
{
base.Refresh();
}
public override void Update( float dt )
{
base.Update( dt );
// todo: hide the particle effect ring when player dead
//Gizmo.Draw.Color = Color.Blue;
//Gizmo.Draw.LineSphere( Player.WorldPosition.WithZ( 50f ), GAZE_RANGE );
_tickTimer += dt;
if ( _tickTimer > TICK_TIME )
{
Vector3 a = new Vector3( Player.Position2D.x, Player.Position2D.y, 32f ) + Player.Model.LocalRotation.Forward * 1f;
Vector3 b = a + Player.Model.LocalRotation.Forward * GAZE_RANGE;
bool hitSomething = false;
var traceResults = Player.Scene.Trace.Sphere( 10f, a, b ).WithAnyTags( "enemy", "player" ).HitTriggersOnly().RunAll().ToList();
foreach ( var tr in traceResults )
{
var unit = tr.GameObject.GetComponent<Unit>();
if ( unit.IsValid() && !unit.IsDying && !unit.IsInanimate && !unit.IsFearful )
{
if ( unit is Player )
continue;
if ( _targetUnit != unit )
{
_targetUnit = unit;
_gazeTime = 0f;
_particleRing.Alpha = 0f;
_particleRingEmitter.Radius = _targetUnit.Radius * 1.4f;
}
_gazeTime += TICK_TIME;
if ( _gazeTime > GetValue( Level, Mod.Time ) )
{
unit.Fear( Player, enemySource: null, lifetime: Player.Stats[PlayerStat.FearLifetime] );
_gazeTime = 0f;
Highlight();
}
hitSomething = true;
break;
}
}
if ( !hitSomething )
{
_gazeTime = MathX.Lerp( _gazeTime, 0f, 0.3f * dt );
_targetUnit = null;
}
_tickTimer -= TICK_TIME;
}
if ( _targetUnit.IsValid() && !_targetUnit.IsFearful )
{
//Gizmo.Draw.Color = Color.Red;
//Gizmo.Draw.LineSphere( _targetUnit.WorldPosition.WithZ( 50f ), 80f * Utils.Map( _gazeTime, 0f, GetValue( Level, Mod.Time ), 1f, 0f ) );
_particles.WorldPosition = _targetUnit.WorldPosition.WithZ( 2f );
_particleRing.Alpha = Utils.Map( _gazeTime, 0f, GetValue( Level, Mod.Time ), 0f, 1f, EasingType.SineIn );
_particleRingEmitter.Radius = Utils.Map( _gazeTime, 0f, GetValue( Level, Mod.Time ), _targetUnit.Radius * 1.4f, _targetUnit.Radius * 0.25f, EasingType.SineIn );
_particleRingEmitter.Rate = 500;
}
else
{
_particles.WorldPosition = _particles.WorldPosition.WithZ( -100f );
_particleRing.Alpha = 0f;
_particleRingEmitter.Rate = 0;
}
}
private static float GetValue( int level, Mod mod, bool isPercent = false )
{
switch ( mod )
{
case Mod.Time:
default:
return 4f - 1f * level;
}
}
public override void Remove( bool restart = false )
{
base.Remove( restart );
if ( _particles != null )
_particles.Destroy();
}
}