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

/// <summary>One-off survey of Last Orders and the ground around it, to plan the
/// back alley expansion. Logs a top-down grid (RS lines):
///   # wall/solid at waist height   . indoor floor (ceiling above)
///   , outdoor floor (open sky)     ~ floor at a different level
///   (space) no floor
/// plus every door-like object in the map.</summary>
public sealed class RestaurantSiteProbe : 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<5 || Scene.GetAllComponents<MapInstance>().Any(m=>m.Active && !m.IsLoaded))return;
        done=true;
        const int step=25,x0=-900,x1=1900,y0=100,y1=2500;const float baseZ=33;
        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 down=Scene.Trace.Ray(new Vector3(x,y,baseZ+220),new Vector3(x,y,baseZ-300)).WithoutTags("paintball_actor","paintball_debris","overdrive_enemy").Run();
                if(!down.Hit){row.Append(' ');continue;}
                float floor=down.HitPosition.z;
                if(floor>baseZ+60){row.Append('#');continue;}
                var waist=new Vector3(x,y,floor+45);
                var solid=Scene.Trace.Sphere(6,waist,waist).WithoutTags("paintball_actor","paintball_debris","overdrive_enemy").Run();
                if(solid.Hit || solid.StartedSolid){row.Append('#');continue;}
                var up=Scene.Trace.Ray(waist,waist+Vector3.Up*400).WithoutTags("paintball_actor","paintball_debris","overdrive_enemy").Run();
                row.Append(MathF.Abs(floor-baseZ)>12 ? '~' : up.Hit ? '.' : ',');
            }
            lines.Add($"{y,5} |{row}");
        }
        var doors=Scene.GetAllObjects(false).Where(g=>g.Name.Contains("door",StringComparison.OrdinalIgnoreCase))
            .Select(g=>$"{g.Name}@{g.WorldPosition.x:0},{g.WorldPosition.y:0},{g.WorldPosition.z:0}");
        Log.Info($"RESTAURANT_SITE x {x0}..{x1} y {y0}..{y1} step {step} (left=low x, top=high y)");
        Log.Info("RESTAURANT_SITE_DOORS "+string.Join(" ; ",doors));
        foreach(var l in lines)Log.Info("RS "+l);
    }
}