Editor/Tool/Subtools/ArchCrossingSubtool.cs
using Editor;
using Sandbox;
namespace Sunless.Architecture;
[Title( "Crossing" ), Icon( "directions_walk" ), Group( "03" )]
public sealed class ArchCrossingSubtool( ArchTool owner ) : ArchSubtool( owner ) {
public override ArchSurface[] Surfaces => new[] { ArchSurface.Pavement, ArchSurface.Kerb, ArchSurface.RoadLine };
CrossingKind kind = CrossingKind.Driveway;
float width = 132f;
protected override bool UsesDrag => false;
protected override string Title() => "Drop Crossing";
protected override string Advice() => "Click a road where the kerb should drop.";
protected override ArchKind? DraftKind => ArchKind.Crossing;
public override void OnEnabled() {
base.OnEnabled();
ArchWorkflow.Restart( Scope( "flow" ) );
}
protected override void OnClick( Vector2 point ) {
if ( Reached( point, out var road, out var frame ) ) {
var crossing = new ArchRoadCrossing {
Id = Owner.Plan.AllocateId(),
Name = $"Crossing{road.Crossings.Count + 1}",
Kind = kind,
Distance = frame.Distance,
Width = width,
Side = Vector2.Dot( point - frame.Flat, ArchBarrierGen.Across( frame ) ) > 0f ? RoadSide.Right : RoadSide.Left
};
road.Crossings.Add( crossing );
FinishDraft( crossing.Id );
Owner.Commit( "Add Crossing" );
}
}
protected override void DrawHover( Vector2 point ) {
base.DrawHover( point );
if ( !Reached( point, out var road, out var frame ) ) {
ArchGhost.Note( new Vector3( point.x, point.y, Owner.LevelHeight ), "click a road" );
return;
}
var reach = road.HalfWidth + road.PavementWidth;
var half = frame.Along * (width * 0.5f);
Gizmo.Draw.Color = ArchGhost.Accent;
Gizmo.Draw.Line( frame.Side( -reach ), frame.Side( reach ) );
ArchGhost.Note( frame.Position, $"{road.Name} {kind} {width / 12f:0.#} ft" );
// A crossing's width is measured ALONG the road.
Gizmo.Draw.Color = ArchGhost.Line;
Gizmo.Draw.Line( frame.Side( -reach ) - half, frame.Side( reach ) - half );
Gizmo.Draw.Line( frame.Side( -reach ) + half, frame.Side( reach ) + half );
}
// Verge not generous radius: the dropped kerb belongs to the pavement it breaks.
bool Reached( Vector2 point, out ArchRoadPart road, out ArchFrame frame ) {
road = Owner.RoadAt( point, candidate => candidate.HalfWidth + candidate.PavementWidth, out frame );
return road is not null;
}
// Two rows: what kind of drop this is, and how much of the kerb it takes. The side is read off which side of
// the centreline the click landed, so it is never asked.
protected override void BuildOptions( ToolSidebarWidget panel ) {
using var flow = ArchWorkflow.In( panel, Scope( "flow" ), Refresh );
flow.Step( "Kind", kind.ToString(), ArchRoadUi.CrossingGlyph( kind ), step =>
ArchRoadUi.Kinds( step.Layout, kind, value => { kind = value; step.Chose(); } ) );
flow.Step( "Width", $"{width / 12f:0.#} ft", "straighten", step =>
step.Layout.Add( ArchPartUi.Number( "Width", width, 132f, value => width = value ) ) );
}
}