Editor/Barriers/ArchBarrierPart.cs

Editor data model for architectural barriers. Defines enums for barrier style and ground behaviour, a bay class for panel overrides, and ArchBarrierSpec which holds all editable parameters (dimensions, panel layout, posts, rails, wear/seed) with computed properties and a shallow copy method.

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

namespace Sunless.Architecture;

// Append only - a member inserted mid-list renumbers the rest and silently restyles every authored barrier.
public enum BarrierStyle
{
	Picket,
	PrecastSlab,
	Palisade,
	Guardrail,
	PostAndRail,
	Bollards,
	// Holds fill rather than standing on it, so ArchFenceGen stands ArchWing on the spline instead of posts.
	Retaining,
	Catenary
}

// Precast bays stay level and staircase; a picket fence rakes.
public enum BarrierGround
{
	Follow,
	Step,
	Level
}

// Overrides what the panel length would have given.
public sealed class ArchBarrierBay
{
	public float Span { get; set; }
	public int Courses { get; set; }
	public bool Gate { get; set; }
}

// Shared by fences and road barriers.
public sealed class ArchBarrierSpec
{
	public BarrierStyle Style { get; set; } = BarrierStyle.Picket;
	public BarrierGround Ground { get; set; } = BarrierGround.Follow;

	public float Height { get; set; } = 40f;
	// Bays are whole panels, remainder a short closing bay - panels don't stretch.
	public float PanelLength { get; set; } = 96f;
	public bool EvenBays { get; set; }
	public List<ArchBarrierBay> Bays { get; set; } = new();

	public float PostSize { get; set; } = 5f;
	// On a precast wall this is what the droppers bolt to.
	public float PostOverhang { get; set; }

	public int Courses { get; set; } = 4;
	public float CourseHeight { get; set; }
	public float PanelThickness { get; set; } = 2.4f;
	public float PlinthHeight { get; set; }

	public int Rails { get; set; } = 2;
	public PicketTop Top { get; set; } = PicketTop.Pointed;

	public int Droppers { get; set; }
	public float DropperLean { get; set; } = 35f;
	public float DropperLength { get; set; } = 16f;
	public int Strands { get; set; } = 6;

	public float GateAt { get; set; }
	public float GateWidth { get; set; }

	// Wear leans and slips, Broken is missing panels, CourseVary steps a bay's height.
	public int Seed { get; set; }
	public float Wear { get; set; }
	public float Broken { get; set; }
	public int CourseVary { get; set; }

	public bool HasGate => GateWidth > 8f;

	// The plinth is part of the height - the stack finishes on Height.
	public float Course => CourseHeight > 0.5f
		? CourseHeight
		: MathF.Max( 2f, (Height - PlinthHeight) / Math.Max( 1, Courses ) );

	public ArchBarrierSpec Copy()
	{
		var copy = (ArchBarrierSpec)MemberwiseClone();
		copy.Bays = Bays.Select( bay => new ArchBarrierBay { Span = bay.Span, Courses = bay.Courses, Gate = bay.Gate } ).ToList();

		return copy;
	}
}