Entities/Blocks/BlockLaserPierce.cs
namespace BlockParty;
/// <summary>
/// A single-beam Laser variant which pierces blocks and authored obstacles. Phase 1 aims randomly;
/// phase 2 aims at the nearest living player with a moderate, randomized velocity lead.
/// Arena walls and living hardened players stop the beam.
/// </summary>
public sealed class BlockLaserPierce : BlockLaser
{
const float MAX_LEAD_TIME = 0.5f;
const float MAX_LEAD_DISTANCE = 48f;
static readonly Color PHASE_1_TRACER = new( 0.52f, 0.24f, 0.28f );
static readonly Color PHASE_1_CORE = new( 1f, 0.72f, 0.28f );
static readonly Color PHASE_1_GLOW = new( 1f, 0.22f, 0.42f );
static readonly Color PHASE_2_TRACER = new( 0.55f, 0.20f, 0.35f );
static readonly Color PHASE_2_CORE = new( 1f, 0.62f, 0.34f );
static readonly Color PHASE_2_GLOW = new( 0.95f, 0.18f, 0.52f );
protected override float BeamGlowLocalZ => Globals.DEPTH_WALL - Globals.DEPTH_BLOCK + 0.35f;
protected override float BeamCoreLocalZ => Globals.DEPTH_WALL - Globals.DEPTH_BLOCK + 0.45f;
protected override float BeamSparkLocalZ => Globals.DEPTH_WALL - Globals.DEPTH_BLOCK + 0.55f;
protected override float DeadlyCoreWidthScale => 0.75f;
protected override float DeadlyGlowWidthScale => 0.8f;
protected override Vector2 BeamOriginPosition => Position + new Vector2( 0f, -8f );
protected override int BeamCountForPhase( int phase ) => 1;
// The beam pierces obstacles, so only a touching arena wall makes an aim pointless.
protected override AimAvoid FreshAimAvoids => AimAvoid.Walls;
protected override Vector2 AimDirectionForPhase( int phase )
{
if ( phase < 2 ) return RandomAimDirection();
Player target = Stage.ClosestLivingPlayer( BeamOriginPosition );
if ( target is null ) return RandomAimDirection();
Vector2 targetVelocity = target.Velocity + new Vector2( target.ExtraVelX, target.ExtraVelY );
Vector2 lead = targetVelocity * Rng.Float( 0f, MAX_LEAD_TIME );
if ( lead.Length > MAX_LEAD_DISTANCE )
lead = Utils.Normalized( lead ) * MAX_LEAD_DISTANCE;
Vector2 direction = Utils.Normalized( target.Position + lead - BeamOriginPosition );
return direction == Vector2.Zero ? RandomAimDirection() : direction;
}
protected override IEnumerable<Entity2D> BeamBlockers()
{
foreach ( Player player in Stage.Players )
if ( player.IsHardened && !player.IsDead )
yield return player;
}
protected override Color TracerColorForPhase( int phase )
=> phase >= 2 ? PHASE_2_TRACER : PHASE_1_TRACER;
protected override Color CoreColorForPhase( int phase )
=> phase >= 2 ? PHASE_2_CORE : PHASE_1_CORE;
protected override Color GlowColorForPhase( int phase )
=> phase >= 2 ? PHASE_2_GLOW : PHASE_1_GLOW;
static Vector2 RandomAimDirection()
=> Utils.Normalized( Utils.RotateVector( Vector2.Down, Rng.Float( 0f, 360f ) ) );
}