Editor/Layers/ArchKindManifest.cs
using System;
using System.Collections.Generic;
using Sandbox;
namespace Sunless.Architecture;
public interface IArchKindManifest {
ArchKind Kind { get; }
Type Payload { get; }
string Slot { get; }
IEnumerable<Type> Hosts { get; }
ArchLayerDomain Domain { get; }
string Label { get; }
string Glyph { get; }
string Subtool { get; }
bool RootsAtPlan { get; }
// False for kinds painted onto their host rather than independently placed
bool Transforms { get; }
string HostOutput { get; }
IEnumerable<ArchKind> Children { get; }
// The inverse of Children — contributed kinds name their parents since the host can't name them
IEnumerable<ArchKind> Parents { get; }
// The layer this part hangs under, read from the part. Zero = plan root.
int Parent( object payload );
int DirectRank { get; }
int IndirectRank { get; }
ArchLayerStage Stage( object payload );
string NameOf( object payload, int id );
string DisplayName( object payload );
bool TryRename( object payload, string name );
IEnumerable<object> Filed( ArchPlan plan, object host );
System.Collections.IList Folder( ArchPlan plan, object host );
bool File( object host, object payload );
bool Unfile( ArchPlan plan, object payload );
IReadOnlyList<List<Vector2>> Loops( object payload );
float Height( object payload, float fallback );
}
// Answers nothing by default, so a manifest states only what its kind has an opinion about. A kind with no shape,
// no children and no name of its own is a handful of lines.
public abstract class ArchKindManifest : IArchKindManifest {
public abstract ArchKind Kind { get; }
public abstract Type Payload { get; }
public virtual string Slot => "";
public virtual IEnumerable<Type> Hosts => Array.Empty<Type>();
public virtual ArchLayerDomain Domain => ArchLayerDomain.Buildings;
public virtual string Label => Kind.Ident;
public virtual string Glyph => "crop_square";
public virtual string Subtool => "";
public virtual bool RootsAtPlan => false;
public virtual bool Transforms => true;
public virtual string HostOutput => "a host output";
public virtual IEnumerable<ArchKind> Children => Array.Empty<ArchKind>();
public virtual IEnumerable<ArchKind> Parents => Array.Empty<ArchKind>();
public virtual int Parent( object payload ) => 0;
public virtual int DirectRank => 200;
public virtual int IndirectRank => 999;
public virtual ArchLayerStage Stage( object payload ) => ArchLayerStage.Shape;
public virtual string NameOf( object payload, int id ) => $"{Label} {id}";
public virtual string DisplayName( object payload ) => null;
public virtual bool TryRename( object payload, string name ) => false;
public virtual IEnumerable<object> Filed( ArchPlan plan, object host ) => Array.Empty<object>();
public virtual System.Collections.IList Folder( ArchPlan plan, object host ) {
var holder = host ?? plan;
return string.IsNullOrEmpty( Slot ) || holder is null
? null
: holder.GetType().GetProperty( Slot )?.GetValue( holder ) as System.Collections.IList;
}
public virtual bool File( object host, object payload ) => false;
public virtual bool Unfile( ArchPlan plan, object payload ) => false;
public virtual IReadOnlyList<List<Vector2>> Loops( object payload ) => Array.Empty<List<Vector2>>();
public virtual float Height( object payload, float fallback ) => fallback;
}
public interface IArchNamed {
string Name { get; set; }
}
public abstract class ArchKindManifest<TPayload> : ArchKindManifest where TPayload : class {
public override Type Payload => typeof( TPayload );
public override string NameOf( object payload, int id ) {
return payload is TPayload and IArchNamed named ? named.Name : base.NameOf( payload, id );
}
public override bool TryRename( object payload, string name ) {
if ( string.IsNullOrWhiteSpace( name ) || payload is not TPayload || payload is not IArchNamed named ) {
return false;
}
named.Name = name;
return true;
}
public override IEnumerable<object> Filed( ArchPlan plan, object host ) {
return Hosting( host ) && Folder( plan, host ) is { } folder ? folder.Cast<object>() : Array.Empty<object>();
}
public override bool File( object host, object payload ) {
if ( payload is not TPayload || !Hosting( host ) || Folder( null, host ) is not { } folder ) {
return false;
}
folder.Add( payload );
return true;
}
public override bool Unfile( ArchPlan plan, object payload ) {
if ( plan is null || payload is not TPayload ) {
return false;
}
foreach ( var host in ArchPlanHosts.Of( plan, Hosts ) ) {
if ( Folder( plan, host ) is { } folder && folder.Contains( payload ) ) {
folder.Remove( payload );
return true;
}
}
return false;
}
bool Hosting( object host ) => host is not null && Hosts.Any( type => type.IsInstanceOfType( host ) );
}
public interface IArchReachesWide {
IEnumerable<object> Wide( ArchPlan plan );
}
public interface IArchNormalizes {
void Normalize( ArchPlan plan );
}