Editor/Porch/ArchPorchArchetypeStep.cs

An editor-side archetype builder step that constructs a porch/canopy for a room in a building plan. It validates the room, fills an ArchPorchPart options structure from the step and room data, attempts to attach a porch, applies spacing and roof/skin rules, and returns a descriptive name or null when skipped.

File Access
namespace Sunless.Architecture;

public sealed class ArchPorchArchetypeStep : IArchArchetypeStepBuilder
{
	public ArchStepKind Kind => ArchStepKind.Canopy;

	public string Build( ArchArchetypeStepContext context )
	{
		var room = context.Room;
		var step = context.Step;

		if ( room is null )
		{
			context.Skipped.Add( "a Canopy step with no room to hang off" );
			return null;
		}

		var options = new ArchPorchPart
		{
			BaseHeight = room.BaseHeight,
			GradeHeight = room.BaseHeight - step.DeckDrop,
			HeadHeight = step.HeadHeight,
			Deck = step.Deck,
			Plinth = step.Deck,
			Posts = step.Posts,
			Rafters = step.Rafters,
			Braces = step.Braces,
			Railings = step.Railings,
			Steps = step.Steps,
			Pitch = step.Rules.RoofPitch,
			Roofed = true
		};

		var placement = ArchPorch.Attach( context.Plan, context.Building, room, context.Kit, context.Min, context.Max, options );

		if ( placement is null )
		{
			context.Skipped.Add( $"a Canopy step over {room.Name} - the drag left no apron outside the walls to cover" );
			return null;
		}

		if ( step.PostSpacing > 1f )
		{
			placement.Porch.PostSpacing = step.PostSpacing;
		}

		if ( placement.Roof is not null )
		{
			ArchArchetypeRules.Roof( placement.Roof, step.Rules );
		}

		ArchArchetypeRules.Skin( placement.Porch.Palette, step.Rules.Palette );

		return $"Canopy {placement.Porch.Name}";
	}
}