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 ) ) );
}