Editor utility that generates wall corner recess modifiers from masonry damage cuts. It iterates damage volumes affecting a wall, computes brick/course parameters and vertical ranges, and returns a list of ArchWallModShape entries describing carved reveals at wall ends.
using System;
using System.Collections.Generic;
using Sandbox;
namespace Sunless.Architecture;
// The recesses a masonry cut leaves at a wall's own corners, expressed as ordinary wall modifiers so the generator
// carves them with the same pass it carves an authored indent with.
public static class ArchWallDamage
{
public static List<ArchWallModShape> CornerRecesses(
ArchPlan plan,
ArchKit kit,
ArchBuilding building,
ArchRoom room,
ArchWall wall,
float edgeFrom,
float edgeTo,
float height,
float thickness )
{
var recesses = new List<ArchWallModShape>();
foreach ( var (cut, volume) in ArchCut.Damage( plan, room.Floor, kit, room.BaseHeight, room.BaseHeight + height, building.Id, ArchCutAffects.Walls, wall.Id ) )
{
if ( cut.ResolvedDamage != ArchDamageKind.Masonry || !cut.MasonryCarves )
{
continue;
}
foreach ( var (point, along, direction) in new[]
{
(wall.Start, edgeFrom, -1f),
(wall.End, edgeTo, 1f)
} )
{
var brick = MathF.Max( 2f, cut.DamageCellWidth );
var course = MathF.Max( 2f, cut.DamageCellLength );
var corner = ArchFootprint.Rect( point - new Vector2( brick * 2f, brick * 2f ), point + new Vector2( brick * 2f, brick * 2f ) );
if ( !ArchFootprint.Overlaps( volume.Footprint, corner ) )
{
continue;
}
var bottom = Math.Clamp( volume.Floor.At( point ) - room.BaseHeight, 0f, height );
var top = Math.Clamp( volume.Ceiling.At( point ) - room.BaseHeight, 0f, height );
var first = (int)MathF.Floor( bottom / course );
var last = (int)MathF.Ceiling( top / course );
for ( var z = first; z < last; z++ )
{
var state = ArchDamage.CourseAt( cut, z, direction );
if ( !ArchDamage.KeepsCourse( cut, wall.Id, z ) )
{
continue;
}
var low = MathF.Max( bottom, z * course );
var high = MathF.Min( top, (z + 1) * course );
var brickSpan = state switch
{
ArchDamage.MasonryCourse.Header => MathF.Max( course * 1.25f, brick * 0.35f ),
_ => brick
};
var chipped = course * (0.35f + ArchBarrierShape.Noise( ArchDamage.Seed( cut ), wall.Id ^ z * 83492791, ArchDamage.StateSalt ) * 0.45f );
var span = brickSpan + chipped;
var from = direction < 0f ? along : along - span;
var to = direction < 0f ? along + span : along;
recesses.Add( new ArchWallModShape
{
Part = new ArchWallModPart
{
Name = "Masonry Reveal",
Kind = ArchWallMod.Indent,
Side = ArchWallSide.Outside,
Field = ArchSurface.Reveal,
Palette = cut.Palette
},
Carves = true,
Sealed = true,
Outside = true,
From = from,
To = to,
Bottom = low,
Top = high,
Depth = MathF.Min( MathF.Max( 0.25f, cut.DamageDepth ), MathF.Max( 0.25f, thickness - 0.5f ) )
} );
}
}
}
return recesses;
}
}