editor/AdvancedMappingTools/Architecture/ScaffoldingEditor.cs

Editor tool that stamps a scaffolding frame into the map: posts, ledgers, optional planks and diagonal bracing, with posts optionally fitted to the ground surface. Exposes length, depth, height, bay width and level count as properties.

namespace AdvancedMappingTools;
/// <summary>
/// A scaffolding frame: posts, ledgers, planks and optional diagonal bracing.
/// </summary>
[ToolCategory( "Buildings" )]
[Title( "Scaffolding" ), Icon( "grid_view" )]
public sealed partial class ScaffoldingEditor( PrimitiveTool tool ) : GroundAwareStampEditor( tool )
{
	[Property, Group( "Scaffolding" ), Range( 32, 2048, slider: false ), WideMode]
	public float Length { get; set; } = 384.0f;

	[Property, Group( "Scaffolding" ), Range( 8, 1024, slider: false ), WideMode]
	public float Depth { get; set; } = 48.0f;

	[Property, Group( "Scaffolding" ), Range( 16, 1024, slider: false ), WideMode]
	public float Height { get; set; } = 192.0f;

	[Property, Group( "Scaffolding" ), Title( "Bay Width" ), Range( 16, 256, slider: false ), WideMode]
	public float BayWidth { get; set; } = 96.0f;

	[Property, Group( "Scaffolding" ), Range( 1, 16, slider: false ), WideMode]
	public int Levels { get; set; } = 3;

	[Property, Group( "Scaffolding" ), Title( "Planks" ), WideMode]
	public bool Planks { get; set; } = true;

	[Property, Group( "Scaffolding" ), Title( "Bracing" ), WideMode]
	public bool Bracing { get; set; } = true;

	protected override void Stamp( MeshKit kit, Transform frame, Random random )
	{
		var length = MathF.Max( Length, 32.0f );
		var depth = MathF.Max( Depth, 8.0f );
		var height = MathF.Max( Height, 16.0f );
		var bay = Math.Clamp( BayWidth, 16.0f, 256.0f );
		var levels = Math.Clamp( Levels, 1, 16 );
		var step = height / levels;
		var posts = MathF.Max( MathF.Round( length / bay ), 1.0f );
		var read = FitToGround ? (GroundRead ?? SurfaceSolver.Begin( SceneEditorSession.Active?.Scene ?? Game.ActiveScene )) : null;
		void Post( float x, float y )
		{
			var bottom = 0f;
			if ( read != null )
			{
				var sample = read.Probe( frame.PointToWorld( new Vector3( x, y + 2, height ) ), Vector3.Down, Options() );
				if ( read.AcceptBelow( sample, frame.PointToLocal( sample.Position ).z, height ) )
					bottom = frame.PointToLocal( sample.Position ).z;
			}

			kit.Box( new( x - 2, y, bottom ), new( x + 2, y + 4, height ) );
		}

		for ( var i = 0; i <= posts; i++ )
		{
			var x = -length * 0.5f + length * i / posts;
			Post( x, -depth * .5f );
			Post( x, depth * .5f - 4 );
		}

		for ( var level = 1; level <= levels; level++ )
		{
			var z = step * level;
			kit.Box( new Vector3( -length * 0.5f, -depth * 0.5f, z - 2 ), new Vector3( length * 0.5f, -depth * 0.5f + 4, z + 2 ) );
			kit.Box( new Vector3( -length * 0.5f, depth * 0.5f - 4, z - 2 ), new Vector3( length * 0.5f, depth * 0.5f, z + 2 ) );
			if ( Planks )
				kit.Box( new Vector3( -length * 0.5f, -depth * 0.5f + 4, z - 1 ), new Vector3( length * 0.5f, depth * 0.5f - 4, z + 2 ) );
		}

		BuildSurfaceMounts( kit, frame );
		if ( !Bracing )
			return;
		for ( var level = 0; level < levels; level++ )
		{
			var z0 = step * level + 4;
			var z1 = step * (level + 1) - 4;
			if ( z1 <= z0 )
				continue;
			kit.Beam( new Vector3( -length * 0.5f, -depth * 0.5f + 2, z0 ), new Vector3( -length * 0.5f + bay, -depth * 0.5f + 2, z1 ), 2.0f, 4.0f );
			kit.Beam( new Vector3( length * 0.5f, depth * 0.5f - 2, z0 ), new Vector3( length * 0.5f - bay, depth * 0.5f - 2, z1 ), 2.0f, 4.0f );
		}
	}
}