editor/AdvancedMappingTools/Placement/PropEditors.cs

Editor tool classes for the Advanced Mapping Tools addon. PropEditor is a shared base for primitive stamping tools that builds a preview mesh from a part generator and places each prop as a separate object. StreetPropEditor, FurnitureEditor, EquipmentEditor and BarricadeEditor bind it to the street, interior, industrial and defense generators.

namespace AdvancedMappingTools;
/// <summary>
/// Every click places one prop as an object of its own, made of independent parts.
/// </summary>
public abstract class PropEditor( PrimitiveTool tool ) : StampEditor( tool )
{
	public override IReadOnlySet<string> UnusedOptions { get; } = new HashSet<string> { "Surface", "Trim", "Variation" };

	protected abstract IPartGenerator Generator { get; }

	public abstract PropSettings Props { get; }

	public override object ExtraSettings => Props;

	protected override bool PlacesObjects => true;

	protected override void Stamp( MeshKit kit, Transform frame, Random random ) { }

	void Prepare()
	{
		Props.MainMaterial = PartTool.Pick( Props.MainMaterial, SurfaceMaterial );
		Props.MetalMaterial = PartTool.Pick( Props.MetalMaterial, TrimMaterial );
		Props.DetailMaterial = PartTool.Pick( Props.DetailMaterial, TrimMaterial );
	}

	IEnumerable<Transform> Frames()
	{
		var random = new Random( Seed );

		foreach ( var point in Points )
			yield return new Transform( point.Position, Rotation.FromYaw( Yaw + ((float)random.NextDouble() - 0.5f) * RandomTurn * 2.0f ) );
	}

	protected override PolygonMesh BuildPreview()
	{
		Prepare();
		var plan = Props is IAnchorAwareSettings { MountsEnabled: true } ? null : PartService.Plan( Generator, Props );
		var result = new PolygonMesh();
		foreach ( var frame in Frames() )
			result.MergeMesh( (plan ?? PartService.Plan( Generator, Props, surface: SurfaceIntegration.Context( Props, SceneEditorSession.Active?.Scene, frame ) )).Flatten( frame ), Transform.Zero, out _, out _, out _ );
		return result;
	}

	public override bool ChangesOtherSettings( string property ) => Props is IAnchorAwareSettings && property is "MountsEnabled" or "MountBolts" or "MountConnector";

	public override void OnUpdate( SceneTrace trace )
	{
		base.OnUpdate( trace );
		if ( Props is IAnchorAwareSettings { MountsEnabled: true, ShowMountDebug: true } aware )
			SurfaceDebug.Draw( aware.MountOwner );
	}

	protected override void PlaceObjects()
	{
		Prepare();

		foreach ( var frame in Frames() )
			PartTool.Place( Generator, GeneratorRegistry.Load( Generator, GeneratorRegistry.Save( Props ) ), frame );
	}
}

/// <summary>
/// Lamps, signs, benches, bins, wrecks and everything else along a street.
/// </summary>
[ToolCategory( "Street" )]
[Title( "Street Prop" ), Icon( "signpost" )]
public sealed class StreetPropEditor( PrimitiveTool tool ) : PropEditor( tool )
{
	readonly StreetGenerator _generator = new();

	public StreetSettings Settings { get; } = new();

	protected override IPartGenerator Generator => _generator;

	public override PropSettings Props => Settings;
}

/// <summary>
/// Tables, beds, shelves, kitchens and bathrooms.
/// </summary>
[ToolCategory( "Interior" )]
[Title( "Furniture" ), Icon( "chair" )]
public sealed class FurnitureEditor( PrimitiveTool tool ) : PropEditor( tool )
{
	readonly InteriorGenerator _generator = new();

	public InteriorSettings Settings { get; } = new();

	protected override IPartGenerator Generator => _generator;

	public override PropSettings Props => Settings;
}

/// <summary>
/// Barrels, pallets, generators, tanks, scaffolds and roof equipment.
/// </summary>
[ToolCategory( "Industrial" )]
[Title( "Equipment" ), Icon( "precision_manufacturing" )]
public sealed class EquipmentEditor( PrimitiveTool tool ) : PropEditor( tool )
{
	readonly IndustrialGenerator _generator = new();

	public IndustrialSettings Settings { get; } = new();

	protected override IPartGenerator Generator => _generator;

	public override PropSettings Props => Settings;
}

/// <summary>
/// Barricades, boarded windows, concrete blocks and guard booths.
/// </summary>
[ToolCategory( "Defense" )]
[Title( "Barricade" ), Icon( "fence" )]
public sealed class BarricadeEditor( PrimitiveTool tool ) : PropEditor( tool )
{
	readonly DefenseGenerator _generator = new();

	public DefenseSettings Settings { get; } = new();

	protected override IPartGenerator Generator => _generator;

	public override PropSettings Props => Settings;
}