Npcs/Diner/DinerWanderSchedule.cs
using Sandbox.Npcs.Tasks;
namespace Sandbox.Npcs.Schedules;
/// <summary>
/// Relaxed ambient stroll around the café. This is the calm cousin of DinerMillSchedule:
/// where milling is the agitated "there's no free seat!" loiter, wandering is a content
/// customer stretching their legs between sittings, so the crowd feels alive.
///
/// Ported from ScientistWanderSchedule, with two diner-appropriate changes:
/// • Anchored to HomeArea (not current position) so customers stay around the diner
/// instead of drifting across the map.
/// • Diner-flavoured lines.
///
/// Completes quickly (move → maybe say → short wait) so GetSchedule re-evaluates often,
/// exactly like the scientist idle loop. Relies on DinerNpc.Scare() → EndCurrentSchedule()
/// to interrupt mid-stroll if a threat appears, matching DinerMillSchedule.
/// </summary>
public class DinerWanderSchedule : ScheduleBase
{
private static readonly string[] WanderLines =
[
"Lovely spot for a stroll.",
"Think I'll stretch my legs.",
"I'll just take the air.",
"*hums contentedly*",
"Wonder if the special's still on.",
"Back in a moment.",
"Beautiful morning for it.",
"Might pop to the counter.",
];
protected override void OnStart()
{
var diner = Npc as Sandbox.Npcs.Diner.DinerNpc;
var home = diner?.HomeArea ?? Npc.WorldPosition;
var wanderRadius = diner?.MillRadius ?? 300f;
var randomDir = Vector3.Random.WithZ( 0 ).Normal;
// Don't stroll straight at the nearest player — sidestep away with some spread.
var nearest = Npc.Senses.Nearest;
if ( nearest.IsValid() )
{
var toPlayer = (nearest.WorldPosition - Npc.WorldPosition).WithZ( 0 ).Normal;
if ( randomDir.Dot( toPlayer ) > 0.3f )
randomDir = (-toPlayer + Vector3.Random.WithZ( 0 ).Normal * 0.5f).WithZ( 0 ).Normal;
}
// Wider spread than milling (0.3–1.0 × radius) so wandering reads as real movement.
var target = home + randomDir * Game.Random.Float( wanderRadius * 0.3f, wanderRadius );
if ( Npc.Scene.NavMesh.GetClosestPoint( target ) is { } navPoint )
AddTask( new MoveTo( navPoint, 20f ) );
else
AddTask( new MoveTo( target, 20f ) );
if ( Npc.Speech.CanSpeak && Game.Random.Float() < 0.25f )
{
var line = WanderLines[Game.Random.Int( 0, WanderLines.Length - 1 )];
AddTask( new Say( line, 2.5f ) );
}
AddTask( new Wait( Game.Random.Float( 1f, 2.5f ) ) );
}
}