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.
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;
}
}