Editor/Carve/ArchCutPart.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;
namespace Sunless.Architecture;
public enum CutProfile {
Box,
Steps,
Passage,
Poly
}
public enum CutEnclosure {
None,
Walls
}
public enum CutHead {
None,
Cap,
Coping,
Housing
}
public enum ArchDamageKind {
None,
Masonry,
SurfaceSpall,
MissingPanels,
DisplacedPanels,
MixedPanels
}
public enum ArchMasonryPattern {
ExposedEdge,
WornCorner,
BrickPier
}
public enum ArchZoneMode {
Carve,
Damage,
Extrude
}
[Flags]
public enum ArchCutAffects {
None = 0,
Floors = 1 << 0,
Ceilings = 1 << 1,
Foundations = 1 << 2,
Walls = 1 << 3,
WallFittings = 1 << 4,
Roofs = 1 << 5,
Platforms = 1 << 6,
Trims = 1 << 7,
Gutters = 1 << 8,
// Only swallows units the hole covers WHOLE
Windows = 1 << 9,
Beams = 1 << 10,
Lining = 1 << 11,
Roadway = 1 << 12,
Pillars = 1 << 13,
Stairs = 1 << 14,
All = Floors | Ceilings | Foundations | Walls | WallFittings | Roofs | Platforms | Trims | Gutters | Windows | Beams | Lining | Roadway | Pillars | Stairs
}
public sealed class ArchCutSegment {
public Vector2 Start { get; set; }
public Vector2 End { get; set; }
public float Width { get; set; } = 48f;
public float BaseHeight { get; set; }
public float TopHeight { get; set; } = 128f;
public List<Vector2> Loop { get; set; }
public bool HasLoop => Loop is { Count: >= 3 };
public Vector2 Span => End - Start;
public float Length => Span.Length;
public float Yaw => MathF.Atan2( Span.y, Span.x ).RadianToDegree();
public ArchStairAxes Axes => new() { Origin = Start, Yaw = Yaw };
public List<Vector2> Outline() {
if ( HasLoop ) {
return Loop;
}
var axes = Axes;
var across = axes.Across * (MathF.Max( 1f, Width ) * 0.5f);
return new List<Vector2> { Start - across, End - across, End + across, Start + across };
}
public ArchCutSegment Copy() {
return new ArchCutSegment {
Start = Start,
End = End,
Width = Width,
BaseHeight = BaseHeight,
TopHeight = TopHeight,
Loop = Loop?.ToList()
};
}
}
public sealed class ArchCutPart : IArchRamped, IArchPainted, IArchNamed {
public int Id { get; set; }
public string Name { get; set; } = "Cut";
// Zero = hand-authored; nonzero = owned by that layer (deleting the owner deletes this cut)
public int OwnerId { get; set; }
public int Level { get; set; }
public CutProfile Profile { get; set; } = CutProfile.Box;
public ArchCutAffects Affects { get; set; } = ArchCutAffects.All;
public List<ArchCutSegment> Segments { get; set; } = new();
public float StepRise { get; set; }
public float StepGoing { get; set; }
public bool Ramp { get; set; }
public float RampFall { get; set; }
public float RampYaw { get; set; }
public float Rise { get; set; }
public bool SnapAngle { get; set; } = true;
public CutEnclosure Enclosure { get; set; }
public float WallThickness { get; set; } = 8f;
public bool Edge { get; set; }
public float EdgeWidth { get; set; }
public bool GuardsOpenedEdges { get; set; } = true;
public bool BreakEdges { get; set; }
public ArchDamageKind Damage { get; set; }
public float DamageAmount { get; set; } = 0.55f;
public float DamageCellWidth { get; set; } = 24f;
public float DamageCellLength { get; set; } = 48f;
public float DamageDepth { get; set; } = 4f;
public ArchMasonryPattern MasonryPattern { get; set; }
public bool MasonryCarves { get; set; }
public float DamageDrop { get; set; } = 4f;
public float DamageTilt { get; set; } = 12f;
public ArchDamageKind ResolvedDamage => Damage != ArchDamageKind.None
? Damage
: Name?.StartsWith( "Damage", StringComparison.OrdinalIgnoreCase ) == true ? ArchDamageKind.Masonry : ArchDamageKind.None;
public bool IsDamage => ResolvedDamage != ArchDamageKind.None;
public ArchExtrudeKind Extrude { get; set; }
public float ExtrudeDepth { get; set; } = 4f;
public float ExtrudeCourse { get; set; }
public float ExtrudeJoint { get; set; }
public bool ExtrudeBothFaces { get; set; }
public string ExtrudePillarType { get; set; } = "";
// Null inherits the type's footing; set overrides it
public PillarFooting? ExtrudeFooting { get; set; }
public bool IsExtrude => Extrude != ArchExtrudeKind.None;
public ArchZoneMode Mode => IsDamage
? ArchZoneMode.Damage
: IsExtrude ? ArchZoneMode.Extrude : ArchZoneMode.Carve;
public CutHead Head { get; set; }
public float HeadHeight { get; set; } = 96f;
public bool HeadRoof { get; set; } = true;
public float CopingHeight { get; set; } = 4f;
public float CopingOversail { get; set; } = 1.5f;
public ArchPalette Palette { get; set; } = new();
public bool HasContent => Segments.Any( segment => segment.HasLoop || segment.Length > 1f );
public bool Builds => Enclosure != CutEnclosure.None || Head != CutHead.None;
public IEnumerable<ArchCutSegment> Legs() => Segments.Where( segment => segment.HasLoop || segment.Length > 1f );
public IEnumerable<List<Vector2>> Outlines() => Legs().Select( segment => segment.Outline() );
}