Editor/Tunnel/ArchTunnelPart.cs

Editor-side class representing a tunnel segment (part) used in arch/road modeling. Stores geometry parameters (from/to, headroom, springing, clearances), appearance palette, portal/wingwall settings, and provides simple helpers for length, containment check, and shallow copy with palette skinning.

File Access
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;

namespace Sunless.Architecture;

public enum TunnelBore
{
	Box,
	Arch,
	Horseshoe
}

// A span on the road, like a bridge - the lining and portals stand AROUND it.
public sealed class ArchTunnelPart : 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; } = "Tunnel";
	public string Type { get; set; } = "";

	public float From { get; set; }
	public float To { get; set; }

	public TunnelBore Bore { get; set; } = TunnelBore.Horseshoe;
	// A springing at or above headroom is a box however the bore is set.
	public float Headroom { get; set; } = 216f;
	public float Springing { get; set; } = 132f;
	// The lining's inner face, off the road's own reach.
	public float Clearance { get; set; } = 24f;
	public float Lining { get; set; } = 18f;
	// The bore took the ground away, so the invert is what the road stands on.
	public float Invert { get; set; } = 14f;
	// Cheap - it only adds columns to a swept section.
	public int Segments { get; set; } = 6;

	public RoadSide Walkways { get; set; } = RoadSide.Both;
	public float WalkwayWidth { get; set; } = 48f;
	public float WalkwayHeight { get; set; } = 12f;

	public bool Portals { get; set; } = true;
	public float PortalDepth { get; set; } = 36f;
	// Stops the wedge of daylight a flat headwall leaves against an irregular hillside.
	public float Collar { get; set; } = 72f;
	public float PortalRise { get; set; } = 42f;
	public float PortalReach { get; set; } = 72f;
	// Stops the portal reading as a hole in a wall.
	public float Hood { get; set; } = 54f;
	public float HoodDepth { get; set; } = 18f;
	public float WingWall { get; set; } = 168f;
	public float WingSplay { get; set; } = 40f;
	public float Coping { get; set; } = 6f;

	// Bored leaves the hill standing; opened up buries the lining in fill.
	public bool Bored { get; set; } = true;

	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 - same reason as the bridge's.
	public ArchTunnelPart Copy( float from, float to )
	{
		var copy = (ArchTunnelPart)MemberwiseClone();

		copy.From = from;
		copy.To = to;
		copy.Palette = new ArchPalette();

		ArchArchetypeRules.Skin( copy.Palette, Palette );

		return copy;
	}
}