AI/Animals/Actions/EatFoodAction.cs
using System;
namespace HC3;
public sealed class EatFoodAction : BehaviorTreeAction
{
private GameObject _foodSource;
private Vector3 _targetPosition;
private bool _isEating;
[Property] public float SearchRadius { get; set; } = 500f;
[Property] public string FoodTag { get; set; } = "food";
[Property] public float HungerThreshold { get; set; } = 0.4f;
[Property] public RangedFloat EatingDuration { get; set; } = new RangedFloat( 5f, 10f );
[Property] public string EatAnimation { get; set; } = "eat";
public override float Score()
{
if ( !Agent.IsValid() ) return 0f;
var needSystem = Agent.GetComponent<NeedSystem>();
if ( needSystem == null ) return 0f;
var hungerNeed = needSystem.GetNeed( "Hunger" );
if ( hungerNeed == null ) return 0f;
// If already eating, keep eating
if ( _isEating ) return 300f;
// If hunger is high enough, find food
if ( hungerNeed.GetPriority() > HungerThreshold )
{
_foodSource = FindFood();
if ( _foodSource.IsValid() )
{
_targetPosition = _foodSource.WorldPosition;
return 150f;
}
}
return 0f;
}
protected override void OnTreeStart()
{
_isEating = true;
Agent.Tags.Set( "eating", true );
}
protected override void OnTreeStop()
{
_isEating = false;
Agent.Tags.Set( "eating", false );
}
protected override Node BuildTree()
{
if ( !_foodSource.IsValid() )
return null;
return new SequenceNode(
[
new MoveToNode( Agent, _targetPosition, "Going to food"),
new TimedTagNode( Agent, "eating", Random.Float( EatingDuration.Min, EatingDuration.Max ), "Eating food" ),
new RestoreNeedNode(Agent, "Hunger", 0.0f)
] );
}
private GameObject FindFood()
{
// Use Physics.Sphere to find all colliders within search radius
var position = Agent.WorldPosition;
var results = Scene.FindInPhysics( new Sphere( position, SearchRadius ) );
foreach ( var gameObject in results )
{
// Check if this is a valid food source with the right tag
if ( gameObject.IsValid() && gameObject.Tags.Has( FoodTag ) )
{
// Check if we can walk to it
if ( GridManager.IsWalkable( Agent.WorldPosition, gameObject.WorldPosition ) )
{
return gameObject;
}
}
}
return null;
}
}