An editor-side data container for architectural kit parameters and presets. It stores many float/int sizing defaults (walls, roofs, stairs, fittings), lists of preset types, a palette, and loads catalogs in the constructor; provides lookup helpers for openings, road lines and profiles.
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;
namespace Sunless.Architecture;
public sealed class ArchKit
{
public string Name { get; set; } = "default";
// What everything in the plan collides as unless a part says otherwise. Solids by default: the generators know
// their own convex pieces, and a colonnade of them costs the solver a fraction of one concave mesh per part.
public ArchCollisionMode Physics { get; set; } = ArchCollisionMode.Solids;
public float GridSize { get; set; } = 32f;
// Read through ArchContact, never here.
public float ContactBite { get; set; } = 0.4f;
public float GroundClearance { get; set; } = 2f;
public float TerrainPadding { get; set; } = 48f;
public float WallHeight { get; set; } = 128f;
public float WallThickness { get; set; } = 8f;
public float FloorThickness { get; set; } = 8f;
// Deeper than a floor slab on purpose: a ceiling is the body a coffer or a beam zone is taken out of.
public float CeilingThickness { get; set; } = 12f;
public float FoundationDepth { get; set; } = 24f;
public float FoundationOversize { get; set; } = 2f;
public float FoundationRise { get; set; } = 18f;
// How far a break edge wanders off the clean cut, and how deep a sticker window's box reads.
public float BreakJitter { get; set; } = 6f;
public float FalseInteriorDepth { get; set; } = 24f;
// Driveway apron, not a wheelchair ramp - steeper than code to fit a front garden.
public float RampSlope { get; set; } = 0.15f;
public float RampThickness { get; set; } = 5f;
public float RampKerbHeight { get; set; } = 5f;
public float RampKerbWidth { get; set; } = 4f;
public float CasingWidth { get; set; } = 4f;
public float CasingDepth { get; set; } = 1.5f;
public float BaseboardHeight { get; set; } = 6f;
public float BaseboardDepth { get; set; } = 1.5f;
// Height is per wall; only the section is a kit dimension.
public float WallBandHeight { get; set; } = 4f;
public float WallBandDepth { get; set; } = 1.5f;
public float WallCapHeight { get; set; } = 6f;
public float SillDepth { get; set; } = 3f;
public float SillThickness { get; set; } = 2f;
// A burglar bar is on every window in these suburbs, so its pitch is a kit dimension and never a
// per-opening number.
public float GrilleBar { get; set; } = 0.9f;
public float GrilleGap { get; set; } = 5f;
public float GrilleProud { get; set; } = 1.5f;
public float GrilleFrame { get; set; } = 1.6f;
// The lap is deliberately not the casing's depth, or the saddle z-fights it down both jambs.
public float ThresholdProud { get; set; } = 0.6f;
public float ThresholdLap { get; set; } = 2f;
// The shadow under every course comes from geometry - a flat texture can't do this at grazing sun.
public float SidingCourse { get; set; } = 7f;
public float SidingProud { get; set; } = 1.4f;
public float SheetPitch { get; set; } = 9f;
public float SheetCrest { get; set; } = 3.5f;
public float SheetProud { get; set; } = 1.2f;
public float FloorBoardWidth { get; set; } = 6f;
public float FloorBoardGap { get; set; } = 0.25f;
public float FloorBoardThickness { get; set; } = 1.2f;
public float FloorBoardLength { get; set; } = 96f;
public float WainscotHeight { get; set; } = 38f;
public float WainscotProud { get; set; } = 1.1f;
public float WainscotStile { get; set; } = 3.5f;
public float WainscotBay { get; set; } = 26f;
public float ChairRailHeight { get; set; } = 2.6f;
public float ChairRailDepth { get; set; } = 2.4f;
public float FrameFace { get; set; } = 2.6f;
public float FrameDepth { get; set; } = 3.4f;
public float FrameSetback { get; set; } = 1.6f;
public float SashFace { get; set; } = 2.2f;
public float SashDepth { get; set; } = 1.6f;
public float GlazingBar { get; set; } = 0.8f;
public float PorchDeckThickness { get; set; } = 2.5f;
public float PorchPostSize { get; set; } = 7f;
public float PorchBeamDepth { get; set; } = 9f;
public float PorchRailHeight { get; set; } = 34f;
public float PorchCeilingThickness { get; set; } = 2f;
public float PorchFriezeHeight { get; set; } = 7f;
// Frieze and flashing both sit on the house wall, so they share one proud.
public float PorchFriezeDepth { get; set; } = 2f;
public float PorchFlashingHeight { get; set; } = 6f;
public float RoofRafterDepth { get; set; } = 9f;
public float RoofRafterWidth { get; set; } = 3.4f;
public float RoofRafterSpacing { get; set; } = 60f;
public float RoofPurlinDepth { get; set; } = 4f;
public float RoofPurlinWidth { get; set; } = 2.6f;
public float RoofPurlinSpacing { get; set; } = 44f;
public float PorchRafterDepth { get; set; } = 5f;
public float PorchRafterWidth { get; set; } = 2.4f;
public float PorchRafterSpacing { get; set; } = 36f;
public float PorchBraceSize { get; set; } = 3.2f;
// A knee brace is cut square, so far off 1 it reads as a prop, not a brace.
public float PorchBraceRake { get; set; } = 1f;
public float BalusterSpacing { get; set; } = 5.5f;
public float BalusterGap { get; set; } = 2.2f;
public float BalusterScale { get; set; } = 1f;
// Five sides and five rings is the whole silhouette at this fidelity.
public int BalusterSides { get; set; } = 5;
public float PicketWidth { get; set; } = 3.5f;
public float PicketGap { get; set; } = 2.4f;
public float PicketThickness { get; set; } = 1.2f;
public float FencePostSize { get; set; } = 5f;
public float FenceRailDepth { get; set; } = 2.4f;
public float FenceRailHeight { get; set; } = 3.2f;
public float PalisadeWidth { get; set; } = 1.6f;
public float PalisadeGap { get; set; } = 4.5f;
public float PalisadeThickness { get; set; } = 1.2f;
public float PrecastPostDepth { get; set; } = 7f;
public float PrecastPostWidth { get; set; } = 6f;
public float PrecastSlotDepth { get; set; } = 1.8f;
public float PrecastJoint { get; set; } = 0.3f;
public float PrecastPlinthProud { get; set; } = 0.9f;
public float DropperThickness { get; set; } = 0.8f;
public float WireThickness { get; set; } = 0.25f;
// A fraction of the span, so one number sags a short spur and a long crossing alike.
public float CatenarySag { get; set; } = 0.06f;
// Run per rise: a retaining wall leans back into the fill it holds.
public float RetainingBatter { get; set; } = 0.08f;
public float PavementFall { get; set; } = 1.2f;
public float DroppedKerb { get; set; } = 0.8f;
public float KerbTransition { get; set; } = 24f;
// Sampled across the pavement; along the road, knots crowd straights and thin at the turns.
public int CrossoverSegments { get; set; } = 8;
public float EdgeLineInset { get; set; } = 8f;
public float ZebraBar { get; set; } = 18f;
public float ZebraGap { get; set; } = 18f;
public float RoadPadding { get; set; } = 96f;
public float RoadInset { get; set; } = 4f;
// 15 ft is the shallow end of a suburban corner; crossings this close merge into one node.
public float JunctionRadius { get; set; } = 180f;
public float JunctionSpread { get; set; } = 240f;
// Generous on purpose - real minimum clearances are measured in feet.
public float JunctionLevel { get; set; } = 30f;
// A pier ending exactly on grade floats the moment the terrain moves.
public float PierEmbedment { get; set; } = 12f;
// A pier standing on the kerb line is as wrong as one in the lane.
public float PierClearance { get; set; } = 24f;
public float RoofThickness { get; set; } = 6f;
public float RoofPitch { get; set; } = 30f;
public float RoofOverhang { get; set; } = 16f;
public float FasciaHeight { get; set; } = 8f;
// Roof edge protection comes off the same stock everywhere, so only its height is per-roof.
public float RoofGuardBay { get; set; } = 96f;
public float RoofGuardPost { get; set; } = 3f;
// What stands ON a deck: a flue, a vent cowl, a tank. Its base comes off the deck plane, never an
// authored z, so only its own size lives here.
public float PlantHeight { get; set; } = 40f;
public float PlantWidth { get; set; } = 16f;
public float GutterBracketSpacing { get; set; } = 96f;
public float DownpipeBracketSpacing { get; set; } = 48f;
public float DownpipeWallGap { get; set; } = 1f;
// All ladders come off the same stock, so the sections are kit-wide.
public float LadderBar { get; set; } = 2.5f;
public float LadderBracketSpacing { get; set; } = 60f;
public float LadderCageSpacing { get; set; } = 36f;
// Clears the climber, not the ladder.
public float LadderCageReach { get; set; } = 30f;
// Every fixture hung on an elevation comes off the same stock, so its sections are kit-wide and only
// its extent is dragged.
public float BalconyReach { get; set; } = 36f;
public float BalconySlab { get; set; } = 5f;
public float BalconyRail { get; set; } = 40f;
public float EscapeLanding { get; set; } = 48f;
public float EscapeWidth { get; set; } = 36f;
public float ConduitBracketSpacing { get; set; } = 60f;
public float StepRise { get; set; } = 8f;
public float StepGoing { get; set; } = 12f;
public float StairWidth { get; set; } = 48f;
public float StringerThickness { get; set; } = 3f;
public float StringerDepth { get; set; } = 14f;
public float HandrailHeight { get; set; } = 36f;
// Clears the climber's head over the nosing: how close the flight has to come to a slab to open it.
public float StairHeadroom { get; set; } = 80f;
public float NewelSize { get; set; } = 4.5f;
public float NewelRise { get; set; } = 6f;
public float StairBalusterSpacing { get; set; } = 5f;
// This close, the flight runs against the wall: suppresses that side's balustrade, picks the socket face.
public float StairWallGap { get; set; } = 10f;
// Well past the stair's width on purpose - a short landing leaves floor nobody can reach.
public float StairCornerReach { get; set; } = 96f;
// How far ONTO the floor the walk off a stair carries before a guard may stand in it. A mouth judged as the
// one edge it leaves over lets the ring close a step past the head, which blocks the stair just as surely.
public float StairWalkClearance { get; set; } = 60f;
// Proud is read through ArchContact so the stair capping and floor nosing agree.
public float WellTrimProud { get; set; } = 0.75f;
public float WellTrimDepth { get; set; } = 3.5f;
public float DoorLeafThickness { get; set; } = 3f;
// A leaf hangs clear of what it closes onto.
public float DoorUndercut { get; set; } = 0.5f;
public List<ArchOpeningPreset> Openings { get; set; }
public List<ArchWallPreset> Walls { get; set; }
public List<ArchProfile> Profiles { get; set; }
public List<ArchRoadLine> RoadLines { get; set; }
public ArchPalette Palette { get; set; } = new();
// A kit is born holding whatever the catalogs ship, so one built in memory offers the same presets as one read
// off disk - and a shelf no catalog claims simply stands empty instead of stocking a list the kit would have to
// know the contents of.
public ArchKit()
{
var shelves = ArchCatalogs.Load();
Openings = shelves.Ships<ArchOpeningPreset>( ArchShelf.Openings );
Walls = shelves.Ships<ArchWallPreset>( ArchShelf.Walls );
Profiles = shelves.Ships<ArchProfile>( ArchShelf.Profiles );
RoadLines = shelves.Ships<ArchRoadLine>( ArchShelf.RoadLines );
}
public ArchOpeningPreset FindOpening( string name )
{
return Openings.FirstOrDefault( preset => string.Equals( preset.Name, name, StringComparison.OrdinalIgnoreCase ) )
?? Openings.FirstOrDefault();
}
// An unnamed line is no line - null, not the first preset.
public ArchRoadLine FindRoadLine( string name )
{
return string.IsNullOrWhiteSpace( name )
? null
: RoadLines.FirstOrDefault( line => string.Equals( line.Name, name, StringComparison.OrdinalIgnoreCase ) );
}
public ArchProfile FindProfile( string name )
{
return Profiles.FirstOrDefault( profile => string.Equals( profile.Name, name, StringComparison.OrdinalIgnoreCase ) )
?? Profiles.FirstOrDefault();
}
}