AI/Animals/Actions/HuntAction.cs
namespace HC3;
public partial class HuntAction : BehaviorTreeAction
{
[Property] public float HungerStopThreshold { get; set; } = 0.15f;
private Agent _target;
public override float Score()
{
if ( !Agent.IsValid() )
return 0f;
if ( !Agent.Tags.Has( "escaped" ) )
return 0f;
// Stop hunting if nearly full
var needSystem = Agent.GetComponent<NeedSystem>();
if ( needSystem != null )
{
var hunger = needSystem.GetNeed( "Hunger" );
if ( hunger.IsValid() && hunger.Level < HungerStopThreshold )
return 0f;
}
_target = FindClosestTarget();
return _target.IsValid() ? 999f : 0f;
}
protected override void OnTreeStart()
{
Agent.Controller.IsRunning = true;
Agent.Tags.Set( "hunt", true );
Agent.Tags.Set( "eating", false );
}
protected override void OnTreeStop()
{
Agent.Controller.IsRunning = false;
Agent.Tags.Set( "hunt", false );
Agent.Tags.Set( "eating", false );
}
protected override Node BuildTree()
{
if ( !_target.IsValid() )
return null;
return new SequenceNode(
[
// Follow the target
new FollowTargetNode( Agent, _target.GameObject, 24f, 32f, "Hunting down target" ),
// Attack the target with animation
new TimedSequenceNode(
Agent,
"Run_Attack",
1f,
onStart: () => {
SetTagFor( "attack", 1f );
},
conditionCheck: () => _target.IsValid() &&
_target.WorldPosition.DistanceSquared( Agent.WorldPosition ) < 64f * 64f,
reason: "Attacking prey",
icon: "gavel"
),
// Eat the target and restore hunger
new TimedSequenceNode(
Agent,
"Eating",
3.0f,
onStart: () => {
_target.Damage( 999 );
SetTagFor( "eating", 2f );
},
reason: "Consuming prey",
icon: "restaurant"
),
// Reduce hunger after eating
new RestoreNeedNode( Agent, "Hunger", -0.5f, isRelative: true )
] );
}
async void SetTagFor( string tag, float time )
{
Agent.Tags.Set( tag, true );
await GameTask.DelaySeconds( time );
Agent.Tags.Set( "attack", false );
}
private Agent FindClosestTarget()
{
Agent closest = null;
float closestDist = float.MaxValue;
foreach ( var x in Scene.GetAll<Agent>() )
{
if ( x is Animal ) continue;
if ( x == Agent || !x.IsValid() ) continue;
if ( x.Building.IsValid() ) continue;
float dist = x.WorldPosition.DistanceSquared( Agent.WorldPosition );
if ( dist < closestDist )
{
closestDist = dist;
closest = x;
}
}
return closest;
}
}