Editor/Road/ArchBridgePart.cs

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.

File Access
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;
	}
}