Npcs/Tasks/SitDown.cs
using Sandbox.Npcs.Layers;

namespace Sandbox.Npcs.Tasks;

/// <summary>
/// Stops the NPC at the chair position and plays the seated idle animation.
/// Uses Npc.Renderer directly to set animation params, matching AnimationLayer's pattern.
/// </summary>
public class SitDown : TaskBase
{
	private readonly GameObject _chair;
	private bool _seated;

	public SitDown( GameObject chair )
	{
		_chair = chair;
	}

	protected override void OnStart()
	{
		if ( !_chair.IsValid() ) return;

		// Stop moving
		Npc.Navigation.Agent?.Stop();
		Npc.Navigation.WishSpeed = 0f;

		// Face the chair's forward direction
		var chairFwd = _chair.WorldRotation.Forward.WithZ( 0 );
		if ( chairFwd.Length > 0.01f )
			Npc.GameObject.WorldRotation = Rotation.LookAt( chairFwd, Vector3.Up );

		// Snap to the chair's sit node if present
		var sitNode = _chair.Children.FirstOrDefault( c => c.Name == "sit" );
		if ( sitNode.IsValid() )
			Npc.GameObject.WorldPosition = sitNode.WorldPosition;

		// Set seated animation via the renderer, same as AnimationLayer does internally
		Npc.Renderer?.Set( "b_sit", true );

		_seated = true;
	}

	protected override TaskStatus OnUpdate()
	{
		if ( !_chair.IsValid() )
			return TaskStatus.Failed;

		return _seated ? TaskStatus.Success : TaskStatus.Running;
	}

	protected override void OnEnd()
	{
		// NOTE: this task returns Success immediately, so OnEnd fires the moment we hand off to
		// the next task (e.g. SeatIdle) — while the NPC is still meant to be seated. So we do
		// NOT clear the seated pose or restore movement here. The owning schedule
		// (e.g. DinerSitSchedule.OnEnd) is responsible for standing the NPC back up.
	}
}