Editor/Porch/ArchPorchPart.cs

Editor-side model for a porch architectural part. Defines ArchPorchPart with properties controlling appearance and components (legs, stairs, pillars, trims, palette, roof/doors, structural flags) and small supporting types PorchStanding and ArchPorchLeg.

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

namespace Sunless.Architecture;

// A wrap-around is several of these sharing one deck level and one roof.
public sealed class ArchPorchLeg
{
	public Vector2 Min { get; set; }
	public Vector2 Max { get; set; }
}

// What the deck leans on. Against subtracts the storey's shell, which is the veranda wrapping a house;
// Free keeps the drag whole, which is the only way a deck stands inside a room or out in a yard.
public enum PorchStanding
{
	Against,
	Free
}

// A porch is a HOST, exactly as a room is: its flights, columns and runs are real child layers with their
// own rows, handles, palettes and delete, rather than pieces baked into one generator. RoofId is only
// where the section the cover pass made is standing - Roofed is the authored intent it re-derives from.
public sealed class ArchPorchPart : 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; } = "Porch";
	public List<ArchPorchLeg> Legs { get; set; } = new();
	public PorchStanding Standing { get; set; }
	public float BaseHeight { get; set; }
	public float GradeHeight { get; set; }
	public float HeadHeight { get; set; } = 100f;

	public bool Plinth { get; set; } = true;
	// Off, the same part is a canopy over whatever ground is there.
	public bool Deck { get; set; } = true;
	public bool Posts { get; set; } = true;
	public float PostSpacing { get; set; } = 96f;
	public bool Beam { get; set; } = true;
	public bool Rafters { get; set; }
	// Under a postless canopy they are the only thing carrying it.
	public bool Braces { get; set; }
	public bool Railings { get; set; } = true;
	// Seeds one flight when the porch is placed. The list is the truth afterwards - delete the row and it
	// stays deleted, drag another in and both stand.
	public bool Steps { get; set; } = true;
	// A porch is an entrance, so it stands the door in the wall it fronts rather than leaving it as a second
	// drag to remember - ArchPorchAffector owns it and re-derives which elevation it lands on every commit.
	public bool Door { get; set; } = true;
	// Blank takes the kit's first door.
	public string DoorPreset { get; set; } = "";
	public bool Roofed { get; set; } = true;
	// 0 takes the porch's shallow default.
	public float Pitch { get; set; }
	public int RoofId { get; set; }

	public List<ArchStairPart> Stairs { get; set; } = new();
	public List<ArchPillarPart> Pillars { get; set; } = new();
	public List<ArchTrimPart> Trims { get; set; } = new();

	public ArchPalette Palette { get; set; } = new();

	public float DeckDrop => MathF.Max( 0f, BaseHeight - GradeHeight );

	public IEnumerable<object> Children => Stairs.Cast<object>().Concat( Pillars ).Concat( Trims );
}