RestaurantLayoutProbe.cs
using Sandbox;
using System;
namespace PaintballBaddies;

/// <summary>One-off layout dump for Last Orders: logs a coarse top-down map of
/// walkable navigation, doors, player spawns and kitchen entrances.</summary>
public sealed class RestaurantLayoutProbe : Component
{
    [Property] public bool SurveyEnabled {get;set;}
    private float wait;private bool done;
    protected override void OnUpdate()
    {
        if(!Game.IsEditor || !SurveyEnabled || done || ArenaSelection.Current(Scene)!=ArenaSelection.Restaurant)return;
        wait+=Time.Delta;
        if(wait<4 || Scene.NavMesh.IsGenerating || Scene.GetAllComponents<MapInstance>().Any(m=>m.Active && !m.IsLoaded))return;
        done=true;
        const int step=40;int x0=-500,x1=1500,y0=500,y1=2100;
        var doors=Scene.GetAllObjects(true).Where(g=>g.Name.Contains("door")).Select(g=>g.WorldPosition).ToArray();
        var spawns=ArenaMap.Spawns(Scene).Select(s=>s.WorldPosition).ToArray();
        var lines=new List<string>();
        for(int y=y1;y>=y0;y-=step)
        {
            var row=new System.Text.StringBuilder();
            for(int x=x0;x<=x1;x+=step)
            {
                var c=new Vector3(x,y,33);
                char ch=' ';
                var p=Scene.NavMesh.GetClosestPoint(c,step*.6f);
                if(p.HasValue && MathF.Abs(p.Value.z-33)<60)ch='.';
                if(MultiplayerSession.KitchenEntrances.Any(k=>(k.WithZ(0)-c.WithZ(0)).Length<step*.6f))ch='K';
                if(doors.Any(d=>(d.WithZ(0)-c.WithZ(0)).Length<step*.6f))ch='D';
                if(spawns.Any(s=>(s.WithZ(0)-c.WithZ(0)).Length<step*.6f))ch='P';
                row.Append(ch);
            }
            lines.Add($"{y,5} |{row}");
        }
        Log.Info("RESTAURANT_LAYOUT x from "+x0+" to "+x1+" step "+step+" (left=low x); doors="+string.Join(";",doors.Select(d=>$"{d.x:0},{d.y:0}"))+" spawns="+string.Join(";",spawns.Select(s=>$"{s.x:0},{s.y:0}")));
        foreach(var l in lines)Log.Info("RL "+l);
    }
}