AI: Planetary items page — ZeroSpace/ZeroSpace.Build/Program.cs
This commit is contained in:
@@ -71,6 +71,7 @@ if (skipped.Count > 0)
|
||||
|
||||
var starSystems = new List<StarSystemSource>();
|
||||
var systemMasteries = new List<SystemMasterySource>();
|
||||
var planetaryMasteryItems = new List<PlanetaryMasteryItemSource>();
|
||||
var skippedDocs = new List<string>();
|
||||
var usedIds = new HashSet<string>(StringComparer.Ordinal);
|
||||
|
||||
@@ -104,19 +105,66 @@ foreach (var path in Directory.EnumerateFiles(docsRoot, "*.md", SearchOption.All
|
||||
skippedDocs.Add($"{Path.GetFileName(path)} ({reason})");
|
||||
}
|
||||
}
|
||||
else if (string.Equals(categoryValue.Scalar, "PlanetaryMasteryItem", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (TryReadPlanetaryMasteryItem(path, frontMatter, body, usedIds, out var item, out var reason))
|
||||
{
|
||||
planetaryMasteryItems.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
skippedDocs.Add($"{Path.GetFileName(path)} ({reason})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
starSystems.Sort((left, right) => string.Compare(left.Name, right.Name, StringComparison.OrdinalIgnoreCase));
|
||||
systemMasteries.Sort((left, right) => string.Compare(left.Name, right.Name, StringComparison.OrdinalIgnoreCase));
|
||||
planetaryMasteryItems.Sort((left, right) => string.Compare(left.Name, right.Name, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
var starSystemsPath = Path.Combine(modelRoot, "StarSystems.g.cs");
|
||||
var systemMasteriesPath = Path.Combine(modelRoot, "SystemMasteries.g.cs");
|
||||
var planetaryMasteryItemsPath = Path.Combine(modelRoot, "PlanetaryMasteryItems.g.cs");
|
||||
|
||||
File.WriteAllText(starSystemsPath, GenerateStarSystems(starSystems), Encoding.UTF8);
|
||||
File.WriteAllText(systemMasteriesPath, GenerateSystemMasteries(systemMasteries), Encoding.UTF8);
|
||||
File.WriteAllText(planetaryMasteryItemsPath, GeneratePlanetaryMasteryItems(planetaryMasteryItems), Encoding.UTF8);
|
||||
|
||||
Console.WriteLine($"Generated {starSystems.Count} star systems: {Path.GetRelativePath(solutionRoot, starSystemsPath)}");
|
||||
Console.WriteLine($"Generated {systemMasteries.Count} system masteries: {Path.GetRelativePath(solutionRoot, systemMasteriesPath)}");
|
||||
Console.WriteLine($"Generated {planetaryMasteryItems.Count} planetary mastery items: {Path.GetRelativePath(solutionRoot, planetaryMasteryItemsPath)}");
|
||||
|
||||
// Copy item icons next to the markdown docs into the web app's wwwroot so the
|
||||
// items page can reference them. Images are copied, never moved: the docs folder
|
||||
// stays the source of truth.
|
||||
var itemImagesDir = Path.Combine(solutionRoot, "ZeroSpace.Web", "wwwroot", "images", "planetary-mastery-items");
|
||||
Directory.CreateDirectory(itemImagesDir);
|
||||
|
||||
var expectedImages = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
var copiedImages = 0;
|
||||
foreach (var item in planetaryMasteryItems)
|
||||
{
|
||||
if (item.ImageFileName is null || item.ImageSourcePath is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
expectedImages.Add(item.ImageFileName);
|
||||
var destination = Path.Combine(itemImagesDir, item.ImageFileName);
|
||||
File.Copy(item.ImageSourcePath, destination, overwrite: true);
|
||||
copiedImages++;
|
||||
}
|
||||
|
||||
foreach (var stale in Directory.EnumerateFiles(itemImagesDir, "*.png"))
|
||||
{
|
||||
if (!expectedImages.Contains(Path.GetFileName(stale)))
|
||||
{
|
||||
File.Delete(stale);
|
||||
Console.WriteLine($"Removed stale item image: {Path.GetRelativePath(solutionRoot, stale)}");
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"Copied {copiedImages} planetary mastery item images to {Path.GetRelativePath(solutionRoot, itemImagesDir)}");
|
||||
if (skippedDocs.Count > 0)
|
||||
{
|
||||
Console.WriteLine($"Skipped {skippedDocs.Count} incomplete docs:");
|
||||
@@ -661,6 +709,51 @@ static bool TryReadSystemMastery(
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool TryReadPlanetaryMasteryItem(
|
||||
string path,
|
||||
IReadOnlyDictionary<string, FrontMatterValue> frontMatter,
|
||||
string body,
|
||||
HashSet<string> usedIds,
|
||||
out PlanetaryMasteryItemSource item,
|
||||
out string reason)
|
||||
{
|
||||
item = default!;
|
||||
|
||||
var levels = new string[5];
|
||||
for (var level = 1; level <= 5; level++)
|
||||
{
|
||||
if (!TryGetRequiredString(frontMatter, $"level_{level}", out var text, out reason))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
levels[level - 1] = text;
|
||||
}
|
||||
|
||||
var name = Path.GetFileNameWithoutExtension(path);
|
||||
|
||||
// An icon with the same file name as the doc (e.g. "Battery.png" next to
|
||||
// "Battery.md") is treated as this item's image when present.
|
||||
string? imageFileName = null;
|
||||
string? imageSourcePath = null;
|
||||
var candidateImagePath = Path.ChangeExtension(path, ".png");
|
||||
if (File.Exists(candidateImagePath))
|
||||
{
|
||||
imageFileName = Path.GetFileName(candidateImagePath);
|
||||
imageSourcePath = candidateImagePath;
|
||||
}
|
||||
|
||||
item = new PlanetaryMasteryItemSource(
|
||||
ToUniqueIdentifier(name, usedIds),
|
||||
name,
|
||||
levels,
|
||||
imageFileName,
|
||||
imageSourcePath,
|
||||
body);
|
||||
reason = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IsMasteryField(string key)
|
||||
{
|
||||
if (string.Equals(key, "category", StringComparison.OrdinalIgnoreCase) ||
|
||||
@@ -766,6 +859,43 @@ static string GenerateSystemMasteries(IReadOnlyList<SystemMasterySource> masteri
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
static string GeneratePlanetaryMasteryItems(IReadOnlyList<PlanetaryMasteryItemSource> items)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine("// <auto-generated />");
|
||||
builder.AppendLine("namespace ZeroSpace.Model;");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("public static class PlanetaryMasteryItems");
|
||||
builder.AppendLine("{");
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
builder.AppendLine($" public static readonly PlanetaryMasteryItemData {item.Id} = new(");
|
||||
builder.AppendLine($" Id: {Literal(item.Id)},");
|
||||
builder.AppendLine($" Name: {Literal(item.Name)},");
|
||||
builder.AppendLine($" Levels: [{string.Join(", ", item.Levels.Select(Literal))}],");
|
||||
builder.AppendLine($" ImageFileName: {Literal(item.ImageFileName)},");
|
||||
builder.AppendLine($" Body: {Literal(item.Body)});");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
builder.AppendLine(" public static IReadOnlyList<PlanetaryMasteryItemData> All { get; } =");
|
||||
builder.AppendLine(" [");
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
builder.AppendLine($" {item.Id},");
|
||||
}
|
||||
|
||||
builder.AppendLine(" ];");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine(" public static IReadOnlyDictionary<string, PlanetaryMasteryItemData> ByName { get; } =");
|
||||
builder.AppendLine(" All.ToDictionary(static item => item.Name, StringComparer.OrdinalIgnoreCase);");
|
||||
builder.AppendLine("}");
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
static string BoolLiteral(bool value)
|
||||
{
|
||||
return value ? "true" : "false";
|
||||
@@ -833,3 +963,11 @@ internal sealed record SystemMasterySource(
|
||||
IReadOnlyList<string> AvailableSystems,
|
||||
IReadOnlyList<(string Key, double Value)> Stats,
|
||||
string Body);
|
||||
|
||||
internal sealed record PlanetaryMasteryItemSource(
|
||||
string Id,
|
||||
string Name,
|
||||
IReadOnlyList<string> Levels,
|
||||
string? ImageFileName,
|
||||
string? ImageSourcePath,
|
||||
string Body);
|
||||
|
||||
Reference in New Issue
Block a user