AI/Animals/Actions/SleepAction.cs
using System;

namespace HC3;

public sealed class SleepAction : BehaviorTreeAction
{
	private Vector3 _sleepSpot;
	private float _sleepDuration;
	private bool _isSleeping;

	[Property] public RangedFloat SleepDuration { get; set; } = new RangedFloat( 20f, 40f );
	[Property] public float EnergyThreshold { get; set; } = 0.3f;

	public override float Score()
	{
		if ( !Agent.IsValid() ) return 0f;

		var needSystem = Agent.GetComponent<NeedSystem>();
		if ( needSystem == null ) return 0f;

		var energyNeed = needSystem.GetNeed( "Energy" );
		if ( energyNeed == null ) return 0f;

		// If already sleeping, keep sleeping
		if ( _isSleeping ) return 300f;

		// If energy is low enough, sleep
		if ( energyNeed.GetPriority() > EnergyThreshold )
		{
			// Find a sleep spot - for now just use current position
			_sleepSpot = Agent.WorldPosition;
			return 150f;
		}

		return 0f;
	}

	protected override void OnTreeStart()
	{
		_sleepDuration = Random.Float( SleepDuration.Min, SleepDuration.Max );
		_isSleeping = true;

		Agent.Tags.Set( "sleep", true );
	}

	protected override void OnTreeStop()
	{
		_isSleeping = false;
		Agent.Tags.Set( "sleep", false );
	}

	protected override Node BuildTree()
	{
		return new SequenceNode( new()
		{
			new MoveToNode(Agent, _sleepSpot, "Finding a place to sleep"),
			new SleepNode(Agent, _sleepDuration),
			new RestoreNeedNode(Agent, "Energy", 0.0f )
		} );
	}
}

file sealed class SleepNode : Node
{
	private readonly Agent Agent;
	private readonly float Duration;
	private TimeUntil _sleepTime;

	public SleepNode( Agent agent, float duration )
	{
		Agent = agent;
		Duration = duration;
	}

	protected override void OnStart()
	{
		_sleepTime = Duration;
	}

	public override Status Tick()
	{
		if ( _sleepTime <= 0 )
			return Status.Success;

		return Status.Running;
	}

	public override ActionDisplayInfo? GetDisplay()
	{
		float progress = 1f - (_sleepTime / Duration).Clamp( 0f, 1f );
		return new ActionDisplayInfo( "hotel_class", "Sleeping...", progress );
	}
}