Station-kind descriptions name only goods a recipe makes or takes

Uranium, Hydrogen (and Bacteria, Fungi, Animal Products) were named by mining, volatiles, agriculture, habitat-support and power descriptions with no recipe behind them. Dropped the claims (smaller than authoring a power chain); batch StationKindDescriptions restates the five kinds in the catalog; test pins the rule.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Nibu 2026-11-11 05:59:44 +00:00
commit dcd27cb3b0
5 changed files with 63 additions and 31 deletions

BIN
GameData/Aetheria.cc (Stored with Git LFS)

Binary file not shown.

View file

@ -1,4 +1,5 @@
using System.Linq;
using System.Text.RegularExpressions;
using GameCult.Caching;
using Xunit;
@ -34,4 +35,32 @@ public sealed class StationKindDescriptionTests
Assert.Contains("Diamond", cache.Get<StationKindData>(StationKindData.KeyOf("mining")).Description);
Assert.Contains("Ammonia", cache.Get<StationKindData>(StationKindData.KeyOf("volatiles")).Description);
}
// Kills: a station-kind description naming a commodity no recipe yields or consumes (Uranium, Hydrogen, Bacteria, Fungi and
// Animal Products were named by the shipped descriptions with no recipe behind them).
[Fact]
public void EveryCommodityAStationKindNamesIsMadeOrTakenByARecipe()
{
using var db = AetherDb.Open();
var c = db.Cache;
var used = c.GetAll<RecipeData>().SelectMany(r => r.Inputs.Select(i => i.Commodity.Key.Value).Append(r.Output.Key.Value)).ToHashSet();
var commodities = c.GetAll<SimpleCommodityData>().ToArray();
Assert.True(commodities.Length >= 20 && used.Count >= 20, $"fixture: the catalog holds its commodities and recipes ({commodities.Length}, {used.Count})");
var kinds = c.GetAll<StationKindData>().ToArray();
Assert.True(kinds.Length >= 17, $"fixture: the catalog holds its station kinds ({kinds.Length})");
var orphans = from kind in kinds
from good in commodities
where Regex.IsMatch(kind.Description ?? "", @"\b" + Regex.Escape(good.Name) + @"\b") && !used.Contains(c.RefOf(good).Key.Value)
select $"{kind.Name} names {good.Name}";
Assert.True(!orphans.Any(), string.Join("\n", orphans));
}
// Kills: the fresh-catalog text and the shipped catalog saying different things about the same kind.
[Fact]
public void TheShippedDescriptionsAreTheAuthoredTable()
{
using var db = AetherDb.Open();
foreach (var id in new[] { "mining", "volatiles", "agriculture", "habitat-support", "power" })
Assert.Equal(StationKindsCatalog.DescriptionOf(id), db.Cache.Get<StationKindData>(StationKindData.KeyOf(id)).Description);
}
}

View file

@ -1,24 +0,0 @@
using System;
using GameCult.Caching;
// Economy content, batch 1c: the mining and volatiles station-kind descriptions name every good their kind now extracts.
// MunitionsFeed gave mining a Diamond recipe and volatiles an Ammonia recipe; the descriptions the station-round bench
// wrote predate them. The same text is StationKindsCatalog.Table's, so a catalog authored fresh says the same thing.
// Sets the description whatever it was, so a rerun converges.
public sealed class ExtractionKindDescriptions : EconomyBatch
{
public override void Author(EconomyAuthor author)
{
foreach (var (id, description) in new[]
{
("mining", StationKindsCatalog.DescriptionOf("mining")),
("volatiles", StationKindsCatalog.DescriptionOf("volatiles")),
})
{
var key = StationKindData.KeyOf(id);
var kind = author.Catalog.Get<StationKindData>(key) ?? throw new InvalidOperationException($"The catalog holds no station kind '{id}'.");
kind.Description = description;
author.Put(kind, key);
}
}
}

View file

@ -0,0 +1,27 @@
using System;
using GameCult.Caching;
// Economy content: station-kind descriptions that name only goods the recipe graph makes or takes. The station-round bench wrote
// descriptions naming Uranium, Hydrogen, Bacteria, Fungi and Animal Products, none of which any recipe yields or consumes; this
// restates the five kinds that named them (mining, volatiles, agriculture, habitat-support, power) as StationKindsCatalog.Table
// has them, so a catalog authored fresh says the same thing. Sets the description whatever it was, so a rerun converges.
public sealed class StationKindDescriptions : EconomyBatch
{
public override void Author(EconomyAuthor author)
{
foreach (var (id, description) in new[]
{
("mining", StationKindsCatalog.DescriptionOf("mining")),
("volatiles", StationKindsCatalog.DescriptionOf("volatiles")),
("agriculture", StationKindsCatalog.DescriptionOf("agriculture")),
("habitat-support", StationKindsCatalog.DescriptionOf("habitat-support")),
("power", StationKindsCatalog.DescriptionOf("power")),
})
{
var key = StationKindData.KeyOf(id);
var kind = author.Catalog.Get<StationKindData>(key) ?? throw new InvalidOperationException($"The catalog holds no station kind '{id}'.");
kind.Description = description;
author.Put(kind, key);
}
}
}

View file

@ -14,19 +14,19 @@ public static class StationKindsCatalog
private static readonly (string id, string name, string description)[] Table =
{
("mining", "Mining Station", "Extracts ore (Rock, New Metals, Gold, Uranium, Potassium) and Diamond from bodies and belts; consumes tools and food."),
("volatiles", "Volatiles Station", "Skims Hydrogen, Methane, Ammonia and Water from gas giants and ice; consumes tools."),
("agriculture", "Agriculture Station", "Grows Organics (Algae, Fungi, Bacteria, Animal Products) from Water and Potassium; feeds residential."),
("mining", "Mining Station", "Extracts ore (Rock, New Metals, Gold, Potassium) and Diamond from bodies and belts; consumes tools and food."),
("volatiles", "Volatiles Station", "Skims Methane, Ammonia and Water from gas giants and ice; consumes tools."),
("agriculture", "Agriculture Station", "Grows Algae from Water and Potassium; feeds residential."),
("processing", "Processing Station", "Refines ore and volatiles into Metals, Alloys and Compounds."),
("fabrication", "Fabrication Station", "Turns Alloys and Compounds into Manufacturing compounds, Assemblies, gear, Ammo and consumer goods."),
("shipyard", "Shipyard", "Assembles hulls from Assemblies and Alloys."),
("depot", "Depot", "Stores and dispatches stock; loads haulers and tenders."),
("trade-hub", "Trade Hub", "Exchanges every bloc's goods between haulers and takes a cut; produces nothing."),
("residential", "Residential Station", "A bloom: consumes food, water, Wearables, Consumables and Luxuries; supplies crews."),
("habitat-support", "Habitat Support Station", "Air, water recycling, life support and medicine for nearby habitats; consumes Bacteria, Water, Compounds."),
("habitat-support", "Habitat Support Station", "Air, water recycling, life support and medicine for nearby habitats; consumes Water and Compounds."),
("urban", "Urban Station", "Dense city and services: market, media, repair; consumes luxuries and food."),
("administrative", "Administrative Station", "Issues quotas, contracts and security; consumes luxuries."),
("power", "Power Station", "Makes reactor fuel, reservoirs and capacitors from Uranium and Hydrogen."),
("power", "Power Station", "Generates and distributes power for nearby stations."),
("research", "Research Station", "Certification, compute, prototypes; consumes Assemblies."),
("salvage", "Salvage Station", "Breaks wrecks into Metals and second-hand gear."),
("black-market", "Black Market", "Buys anything, fences stolen cargo."),