Utility for probing architecture geometry. Computes gap distance, whether a direction is against nearby walls, ray-like reach to nearest wall face considering wall normals, thickness and length, and a helper to test if a mid-point offset lies inside a footprint region.
using System;
using System.Collections.Generic;
using Sandbox;
namespace Sunless.Architecture;
// Flush things start INSIDE a wall's thickness - only counting walls strictly in front reports nothing.
public static class ArchProbe
{
// Off the edge so even-odd is no coin flip; near enough a wall's thickness reads covered.
public const float Step = 2f;
public static float Gap( ArchKit kit ) => MathF.Max( Step, kit?.StairWallGap ?? 10f );
public static bool Against( IEnumerable<ArchWall> walls, ArchKit kit, Vector2 from, Vector2 direction )
{
var gap = Gap( kit );
return Reach( walls, kit, from, direction, gap ) <= gap;
}
// A negative answer means the probe started INSIDE a wall's thickness - flush, not nothing found.
public static float Reach( IEnumerable<ArchWall> walls, ArchKit kit, Vector2 from, Vector2 direction, float limit )
{
var best = float.MaxValue;
if ( walls is null )
{
return best;
}
foreach ( var wall in walls )
{
var length = wall.Length;
if ( length < 1f )
{
continue;
}
// Only a wall square to the probe counts. Loose, and every side of everything reads walled.
var facing = Vector2.Dot( wall.Normal, direction );
if ( MathF.Abs( facing ) < 0.8f )
{
continue;
}
var half = (wall.Thickness > 0f ? wall.Thickness : kit.WallThickness) * 0.5f;
var travel = Vector2.Dot( wall.Normal, wall.Start - from ) / facing;
var face = travel - half;
if ( face < -half - 1f || face > limit || face >= best )
{
continue;
}
var along = Vector2.Dot( from + direction * travel - wall.Start, wall.Direction );
if ( along < -1f || along > length + 1f )
{
continue;
}
best = face;
}
return best;
}
// Footprints wind with the interior on the left, so outward is the right-hand normal.
public static bool Faces( IReadOnlyList<IReadOnlyList<Vector2>> region, Vector2 from, Vector2 to, Vector2 outward )
{
return region is { Count: > 0 } && ArchFootprint.Encloses( region, (from + to) * 0.5f + outward * Step );
}
}