Editor class definitions for arch spans. Defines ArchSpanEnd which stores attachment info for one end of a span, and ArchSpanPart which stores properties for a span between two piers including geometry, visuals and a method to produce a dressing copy.
using System;
using Sandbox;
namespace Sunless.Architecture;
// One end of a span. It names the part it dies into rather than storing the point, so the span is re-derived from
// where that pier stands NOW - a column dragged across the room brings its arch with it and needs no carry.
public sealed class ArchSpanEnd
{
// The pillar or wall it lands on. Zero is a bare point, which is what an end drawn away from anything is.
public int PartId { get; set; }
// Which column of a grid, so the end follows that column rather than the part's origin.
public int ColumnX { get; set; }
public int ColumnY { get; set; }
// Where the gesture landed. The answer for a wall end and for an unattached one, and the tie-break for a grid.
public Vector2 At { get; set; }
}
// What bridges two piers: a beam, an arch, or a cove at each end. It is a layer of its own rather than a field on a
// pillar, because two independently placed columns are two parts and an effect stored on one of them would be
// landing on a host it does not own.
public sealed class ArchSpanPart : IArchCollides, IArchPainted, IArchNamed
{
// Overrides the kit for THIS part alone: one wall that needs its exact holes, one tower that needs none.
public ArchCollisionMode? Collision { get; set; }
public int Id { get; set; }
public string Name { get; set; } = "Span";
public int Level { get; set; }
public ArchSpanEnd From { get; set; } = new();
public ArchSpanEnd To { get; set; } = new();
public PillarSpan Form { get; set; } = PillarSpan.Arch;
// Zero takes the piers' own section, so a span drawn between two columns is as thick as they are without
// anything being typed.
public float Thickness { get; set; }
// How far below the piers' head the span's top sits - room for the slab it hangs under.
public float Drop { get; set; }
// Where the span STARTS, measured off the room floor. Zero lets the form's own rise decide, which means the
// band hangs off whatever the piers happen to reach and two spans set the same way spring at two heights.
// Set, it pins them level and caps the band, which is the only way to author a deliberately thin span.
public float Springing { get; set; }
public float BeamDepth { get; set; } = 18f;
public float Rise { get; set; } = 48f;
public float Ring { get; set; } = 10f;
public int Segments { get; set; } = 10;
public ArchPalette Palette { get; set; } = new();
// Measured down from the top, by the same formula a pillar's own arcade uses - see ArchPillarPart.SpanBand.
public float Band => Form switch
{
PillarSpan.Arch => MathF.Max( 8f, Rise + MathF.Max( 2f, Ring ) ),
PillarSpan.Haunch => MathF.Max( 2f, Rise ),
_ => MathF.Max( 1f, BeamDepth )
};
// The dressing alone - what a placement adopts from the seed it was drawn with, and nothing about where it
// stands. A placement that stamped its gesture on the seed carried that answer into the next one.
public ArchSpanPart Dressing()
{
return new ArchSpanPart
{
Form = Form,
Thickness = Thickness,
Drop = Drop,
Springing = Springing,
BeamDepth = BeamDepth,
Rise = Rise,
Ring = Ring,
Segments = Segments,
Palette = Palette
};
}
}