Units/Seeker.cs
namespace PlanetMeat;
[Group( "Planet Meat - Units" ), Title( "Seeker" ), Icon( "explore" )]
public sealed class Seeker : Component, ITargetableListener
{
[RequireComponent] public NavMeshAgent NavAgent { get; set; }
public Animator Animator => field ??= Components.Get<Animator>( FindMode.EnabledInSelfAndDescendants );
[Property]
private float HappyRange
{
get;
set
{
field = value;
HappyRangeSq = value * value;
}
} = 0.0f;
private float HappyRangeSq
{
set;
get
{
if ( float.IsNaN( field ) )
field = HappyRange * HappyRange;
return field;
}
} = float.NaN;
private Targetable FallbackTarget
{
get
{
foreach ( var f in Scene.GetAllComponents<SeekerFallback>() )
{
if ( f.Components.Get<Targetable>() is Targetable t )
return t;
}
return null;
}
}
public Targetable Target
{
get
{
if ( !(field?.IsValid ?? false) )
field = FallbackTarget;
return field;
}
set;
}
public Targetable MyTargetable => field ??= this.FindNearestUpwards<Targetable>();
public Vector3 NavTarget
{
get;
set
{
if ( !Navving || field != value )
{
Navving = true;
NavAgent.MoveTo( value );
field = value;
}
}
}
public bool Navving
{
get => NavAgent.Enabled;
private set
{
if ( !value )
{
NavAgent.Stop();
Animator.StopMoving();
}
NavAgent.Enabled = value;
}
}
protected override void OnStart()
{
base.OnStart();
Navving = false;
SeekTarget();
if ( Components.Get<Targeter>( FindMode.EnabledInSelfAndDescendants ) is Targeter t )
t.SoleFocus = Target;
}
protected override void OnPreRender()
{
base.OnPreRender();
if ( Navving )
{
var vel = NavAgent.Velocity;
if ( !vel.IsNearlyZero() )
{
if ( Target is Targetable t )
Animator.TurnTo( t.WorldPosition - WorldPosition );
else
Animator.TurnTo( vel.Normal );
Animator.Move( vel, NavTarget );
}
}
}
protected override void OnFixedUpdate()
{
base.OnFixedUpdate();
if ( IsProxy )
return;
if ( Navving )
{
if ( Target is Targetable t )
{
if ( t.WorldPosition.DistanceSquared( WorldPosition ) <= HappyRangeSq )
Navving = false;
else
SeekTarget();
}
}
}
private void SeekTarget()
{
var t = Target;
var targetPos = t.WorldPosition;
if ( Scene.NavMesh.GetClosestPoint( targetPos + (WorldPosition - targetPos).Normal * 0.5f * t.BullseyeRadius ) is Vector3 navToTarget )
NavTarget = navToTarget;
}
public void OnKilled()
{
Navving = false;
}
}