Npcs/Diner/DinerFleeSchedule.cs
using Sandbox.Npcs.Layers;
using Sandbox.Npcs.Tasks;

namespace Sandbox.Npcs.Schedules;

/// <summary>
/// Sprint away from whatever just drove through the breakfast.
/// Lines are specific to the diner context — these people are UPSET about their food.
/// </summary>
public class DinerFleeSchedule : ScheduleBase
{
	public GameObject Source     { get; set; }
	public float      PanicLevel { get; set; } = 0.5f;

	private static readonly string[] PanicLines =
	[
		"MY EGGS!",
		"MY COFFEE!!!",
		"What is WRONG with you?!",
		"That was a FULL ENGLISH!",
		"I hadn't even finished!",
		"THERE'S A CAR IN THE RESTAURANT!",
		"Call the police!",
		"My croissant!!",
		"I was EATING that!",
		"YOU MANIAC!",
		"Every single time...",
		"Not again!!",
		"My mimosa!!!",
		"I'm going to TripAdvisor about this!",
	];

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

		// Run faster the more scared they are (180–380)
		Npc.Navigation.WishSpeed = 180f + 200f * PanicLevel;
		Npc.Animation.ClearLookTarget();

		// Scream immediately
		if ( Npc.Speech.CanSpeak )
		{
			var line = PanicLines[Game.Random.Int( 0, PanicLines.Length - 1 )];
			Npc.Speech.Say( line, 2.5f );
		}

		// Flee away from the vehicle with some spread
		var awayDir = (Npc.GameObject.WorldPosition - Source.WorldPosition).WithZ( 0 ).Normal;
		var spread  = Game.Random.Float( -50f, 50f );
		awayDir     = Rotation.FromAxis( Vector3.Up, spread ) * awayDir;

		var fleeDist   = 400f + 800f * PanicLevel;
		var fleeTarget = Npc.GameObject.WorldPosition + awayDir * fleeDist;

		if ( Npc.Scene.NavMesh.GetClosestPoint( fleeTarget ) is { } navPoint )
			AddTask( new MoveTo( navPoint, 15f ) );
		else
			AddTask( new MoveTo( fleeTarget, 15f ) );

		// After reaching safety, catch breath and mutter something
		AddTask( new Wait( Game.Random.Float( 0.5f, 1.5f ) ) );

		if ( Npc.Speech.CanSpeak )
		{
			var afterLine = AfterFleeLines[Game.Random.Int( 0, AfterFleeLines.Length - 1 )];
			AddTask( new Say( afterLine, 3f ) );
		}
	}

	protected override void OnEnd()
	{
		Npc.Navigation.WishSpeed = 100f;
	}

	protected override bool ShouldCancel() => !Source.IsValid();

	private static readonly string[] AfterFleeLines =
	[
		"Is it safe?",
		"*heavy breathing*",
		"I can't believe this.",
		"Every Sunday!",
		"I'm switching to brunch.",
		"Where's my coffee gone?",
		"This neighbourhood is going downhill.",
	];
}