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.
}
}