Editor/Wall/ArchOpening.cs

An editor-side data model for wall openings (doors, windows, etc.). Defines enums for kinds, glass state and furniture, a shape struct, and a sealed ArchOpening class that stores layout, appearance and behavior properties with a palette.

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

namespace Sunless.Architecture;

public enum OpeningKind
{
	Archway,
	Door,
	DoubleDoor,
	Garage,
	Window,
	Hatch
}

public enum GlassState
{
	Intact,
	Broken
}

// What is fixed over the hole. The burglar bar leads because it is on every window of every house in these
// suburbs; a boarded panel, a roller shutter and a security gate are the same welded grid at another pitch,
// running the other way, at another depth.
public enum OpeningFurniture
{
	None,
	Bars,
	Boarded,
	Shutter,
	Gate
}

// WHERE a hole goes in a wall, in the wall's own along-and-up terms, so a module that wants an entrance can ask
// for one without resolving the unit that fills it.
public readonly struct ArchOpeningShape
{
	public float Offset { get; init; }
	public float Width { get; init; }
	public float Height { get; init; }
	public float SillHeight { get; init; }
}

public sealed class ArchOpening : IArchPainted
{
	public int Id { get; set; }
	// Deleting the part that punched it takes the hole - a stair walking through a wall owns its archway.
	public int OwnerId { get; set; }
	// The cut standing where this unit is. Re-derived every commit like any other effect, so dragging the
	// shaft off the window - or unticking Windows on it - hands the window straight back.
	public int SwallowedBy { get; set; }
	public string Preset { get; set; } = "door";
	public float Offset { get; set; }
	public float Width { get; set; }
	public float Height { get; set; }
	public float SillHeight { get; set; }
	public OpeningKind Kind { get; set; } = OpeningKind.Door;
	public bool Cased { get; set; } = true;
	public bool HasSill { get; set; } = true;
	public float CasingWidth { get; set; }
	public bool Leaf { get; set; } = true;
	public bool FlipHinge { get; set; }
	// Rides the engine's StartOpen - a leaf built open would slide twice as far when used.
	public bool StartOpen { get; set; }
	public bool Glazed { get; set; }
	public bool AutoLayout { get; set; } = true;
	// Units side by side in one opening, each with its own sashes.
	public int Lights { get; set; } = 1;
	public int Sashes { get; set; } = 2;
	// Per sash, not per unit - so 6-over-6 is three across by two down.
	public int PanesAcross { get; set; } = 2;
	public int PanesDown { get; set; } = 3;
	public GlassState Glass { get; set; } = GlassState.Intact;
	// Panes on their own object so fragmenting glass can find them - intent only for now.
	public bool Breakable { get; set; }
	public OpeningFurniture Furniture { get; set; }
	public ArchPalette Palette { get; set; } = new();

	public float Left => Offset - Width * 0.5f;
	public float Right => Offset + Width * 0.5f;
	public float Top => SillHeight + Height;

	// The kind has the last word - a retyped preset keeps numbers that mean nothing.
	public bool IsGlazed => Glazed && Kind.Glazes();
	public bool IsSilled => HasSill && Kind.Sills();
	public bool IsHung => Leaf && Kind.Hangs();
	public OpeningFurniture Fitted => Kind.Carries( Furniture ) ? Furniture : OpeningFurniture.None;
}