Defines ArchBridgePart, a data model for a bridge span in the editor. Stores placement along a road (From, To), dimensional parameters (slab, girder, piers, abutments, rail), style choices, palette and barrier spec, and provides helpers Length, Carries and a shallow Copy that clones some nested objects.
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;
namespace Sunless.Architecture;
// A blade is one wall across the deck.
public enum PierStyle
{
Blade,
Columns,
Piers
}
// A SPAN on the road, not a part - it can never come out a different width from the road.
public sealed class ArchBridgePart : 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; } = "Bridge";
// Re-typable long after the drag that made it.
public string Type { get; set; } = "";
// Distances along the road's centreline, so it moves with the road.
public float From { get; set; }
public float To { get; set; }
// The girder's setback is the shadow line down the fascia.
public float Slab { get; set; } = 14f;
public float Girder { get; set; } = 40f;
public float EdgeBeam { get; set; } = 8f;
// Loop cuts - a soffit in one face can't be edited or lit.
public int Ribs { get; set; } = 3;
// A count overrides the spacing - exactly two piers however long.
public int Bents { get; set; }
public float BentSpacing { get; set; } = 600f;
public PierStyle Pier { get; set; } = PierStyle.Blade;
public int Columns { get; set; } = 2;
public float PierWidth { get; set; } = 60f;
public float PierDepth { get; set; } = 30f;
public float Batter { get; set; } = 4f;
public float Headstock { get; set; } = 20f;
// What carries a real pier is a slab you can see the edge of.
public float Footing { get; set; } = 24f;
public float FootingSpread { get; set; } = 18f;
public bool Abutments { get; set; } = true;
public float AbutmentDepth { get; set; } = 72f;
public float WingWall { get; set; } = 120f;
public float WingSplay { get; set; } = 30f;
// The rail stands ON it rather than on the deck.
public float Upstand { get; set; } = 12f;
public float UpstandWidth { get; set; } = 16f;
public bool Parapet { get; set; } = true;
public ArchBarrierSpec Barrier { get; set; } = new()
{
Style = BarrierStyle.Guardrail,
Ground = BarrierGround.Follow,
Height = 42f,
PanelLength = 120f
};
public ArchPalette Palette { get; set; } = new();
public float Length => MathF.Max( 0f, To - From );
public bool Carries( float distance ) => distance > From && distance < To;
// Cloned, not listed - a field forgotten in a list stops applying to every bridge.
public ArchBridgePart Copy( float from, float to )
{
var copy = (ArchBridgePart)MemberwiseClone();
copy.From = from;
copy.To = to;
copy.Barrier = Barrier.Copy();
copy.Palette = new ArchPalette();
ArchArchetypeRules.Skin( copy.Palette, Palette );
return copy;
}
}