Editor/Stair/ArchExteriorStairAffector.cs
using System;
using System.Linq;
namespace Sunless.Architecture;
// A flight hung on an elevation opens whatever its runs walk through, so it is an effect on a host it does
// not own and obeys the affector contract: the archways are re-cut from where the flight stands NOW on every
// commit, and the group it forms is the scope Breach is then allowed to reach.
//
// Known gap it inherits: Breach tests RUNS only. A landing that straddles a wall - which a switchback dragged
// past the corner of an elevation can do - is not opened, and is not tested either.
public static class ArchExteriorStairAffector {
public static int Resolve( ArchPlan plan, ArchKit kit ) {
var recut = 0;
var kinds = ArchKinds.Load();
foreach ( var building in plan.Buildings.ToList() ) {
foreach ( var part in plan.Filed<ArchExteriorStairPart>( building, kinds ).ToList() ) {
var shape = ArchExteriorStair.Resolve( part, building, kit );
if ( !shape.IsUsable ) {
continue;
}
// Any flight will do as the key: every leg of one switchback is filed under the routing's own id.
ArchStairShape.Breach( plan, shape.Flights[0].Stair, shape.Walk(), kit, ArchLayerGroups.Reach( plan, part.Id, building.Id ) );
Regroup( plan, building, part, kinds );
recut++;
}
}
return recut;
}
// The buildings the flight actually opened, gathered with it and with the one it hangs on. A flight that
// walks nowhere but its own elevation is no relationship worth a folder, so it forms none.
static void Regroup( ArchPlan plan, ArchBuilding home, ArchExteriorStairPart part, ArchKinds kinds ) {
var reached = plan.Buildings.Where( host => host != home && Opened( plan, host, part, kinds ) ).ToList();
if ( reached.Count == 0 ) {
ArchLayerGroups.Leave( plan, part.Id );
return;
}
var join = ArchLayerGroups.Holding( plan, part.Id )
?? ArchLayerGroups.Create( plan, ArchLayerGroups.Named( plan ), ArchAssemblyKind.SharedCore, Array.Empty<int>() );
// The affector is filed last, because it acts on what sits above it in the stack.
var members = reached.Select( host => host.Id ).Prepend( home.Id ).Append( part.Id ).ToList();
foreach ( var member in members ) {
ArchLayerGroups.Leave( plan, member );
}
join.Children.Clear();
join.Children.AddRange( members );
}
static bool Opened( ArchPlan plan, ArchBuilding host, ArchExteriorStairPart part, ArchKinds kinds ) {
return host.Rooms
.SelectMany( room => plan.Filed( ArchKind.Wall, room, kinds ) )
.SelectMany( wall => plan.Filed( ArchKind.Opening, wall, kinds ) )
.Any( opening => opening is ArchOpening archway && archway.OwnerId == part.Id );
}
}