Editor/Output/ArchHost.cs
namespace Sunless.Architecture;
// What the tool cannot know about the game it generates into. A game answers here rather than repainting what the
// tool produced; nothing answering is the tool standing on its own, so every fact has a stock answer.
public interface IArchHost {
// The shader a generated blockout material is written against. A game with its own lighting model names it
// here instead of rewriting every .vmat the tool just wrote.
string BlockoutShader { get; }
// Where the material browser looks for the surface library, relative to the open project's root.
string MaterialLibrary { get; }
// Folder under Assets/ every catalogue is read from and written to. A game that files data beside the asset
// it belongs to names that folder here; the author can still override it in the tool's settings.
string DataRoot { get; }
// Where generated blockout materials are written. The grid texture they sample still ships with the library.
string BlockoutFolder { get; }
}
// Never held between asks, for the reason ArchDoorFitters is not: hotload carries a static over.
public sealed class ArchHost {
// The library's own, because complex.shader tints through g_vColorTint and every generated blockout material
// states its colour as g_flTintColor - on the stock shader the tint was dropped and the lot rendered white.
public const string StockShader = "shaders/arch_blockout.shader";
public const string StockLibrary = "Assets/materials";
public const string StockDataRoot = "arch";
public const string StockBlockoutFolder = "materials/blockout";
public string BlockoutShader { get; private init; } = StockShader;
public string MaterialLibrary { get; private init; } = StockLibrary;
public string DataRoot { get; private init; } = StockDataRoot;
public string BlockoutFolder { get; private init; } = StockBlockoutFolder;
public bool IsStock => BlockoutShader == StockShader && MaterialLibrary == StockLibrary
&& DataRoot == StockDataRoot && BlockoutFolder == StockBlockoutFolder;
// Ordered by name and first answer taken, so two games open in one editor cannot make the result depend on
// which assembly enrolled first. A fact left blank falls back rather than writing an empty shader path.
public static ArchHost Load() {
var spoken = ArchDiscovery.EnrolledByName<IArchHost>().FirstOrDefault();
if ( spoken is null ) {
return new ArchHost();
}
return new ArchHost {
BlockoutShader = Answered( spoken.BlockoutShader, StockShader ),
MaterialLibrary = Answered( spoken.MaterialLibrary, StockLibrary ),
DataRoot = Answered( spoken.DataRoot, StockDataRoot ),
BlockoutFolder = Answered( spoken.BlockoutFolder, StockBlockoutFolder )
};
}
static string Answered( string answer, string stock ) {
return string.IsNullOrWhiteSpace( answer ) ? stock : answer;
}
}