Aetheria/tools/AetherDb/StationReactorSpreadCommand.cs
Nibu 4c6dcfea73 AetherDb station-reactor-spread [apply]: station reactor products regain their sources' quality spread
f458206c7 authored the five per-manufacturer station reactor products with bare
ProductRole entries (the .5 / .15 default). The command copies each source product's
Mean and StandardDeviation per role to a role still at the default, and was applied
to the catalog. TwoAiDigest repinned for the catalog move.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-11 12:43:37 +00:00

82 lines
4 KiB
C#

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using GameCult.Caching;
// station-reactor-spread [apply] (aetheria-release follow-up scenarios-batch4-minors): the per-manufacturer station reactors
// (f458206c7) were authored as scaled copies of each maker's largest reactor, but their products were authored with a bare
// ProductRole { Role } for each role, which is the .5 / .15 default, so they lost the source product's quality spread. This
// gives each station product the Mean and StandardDeviation the source product holds for the same role. It writes only a role
// still at the default pair, so a spread tuned in Studio survives every replay, and a second apply has nothing to do. Replays
// before assembly-from-maker (which derives from role means) when that command has an unauthored maker to fill. Dry run
// unless apply.
public static class StationReactorSpreadCommand
{
// The role pair an unauthored ProductRole carries (ProductRole's field defaults).
private static readonly ProductRole Unauthored = new ProductRole();
// (station product, source product, maker short name): the source is the maker's product of the reactor the station
// design was scaled from.
public static readonly (string Station, string Source, string Maker)[] Rows =
{
("Steadfast Station", "Steadfast", "R&D"),
("Manhattan Station", "Manhattan", "Zhestokost"),
("Notorious Station", "Notorious", "Lucent"),
("Vulcan Station", "Vulcan", "AU"),
("MoveOnPro Station", "MoveOnPro", "NiteLife"),
};
public static int Run(CultCache cache, bool apply, TextWriter output, (string Station, string Source, string Maker)[] rows = null)
{
var products = cache.GetAll<FactionProductData>().ToArray();
FactionProductData Find(string name, string maker)
{
var found = products.Where(p => p.Name == name && cache.Get(p.Manufacturer)?.ShortName == maker).ToArray();
if (found.Length != 1) throw new InvalidOperationException($"Station reactor row \"{name}\" by {maker}: {found.Length} products match.");
return found[0];
}
var changed = new List<FactionProductData>();
foreach (var (stationName, sourceName, maker) in rows ?? Rows)
{
var station = Find(stationName, maker);
var source = Find(sourceName, maker);
foreach (var role in station.Roles)
{
var from = source.Roles.SingleOrDefault(r => r.Role == role.Role)
?? throw new InvalidOperationException($"\"{sourceName}\" holds no \"{role.Role}\" role to copy to \"{stationName}\".");
if (role.Mean != Unauthored.Mean || role.StandardDeviation != Unauthored.StandardDeviation) continue;
if (from.Mean == role.Mean && from.StandardDeviation == role.StandardDeviation) continue;
output.WriteLine($"{stationName} ({maker}) {role.Role}: {role.Mean:0.###}±{role.StandardDeviation:0.###} -> {from.Mean:0.###}±{from.StandardDeviation:0.###}");
role.Mean = from.Mean;
role.StandardDeviation = from.StandardDeviation;
if (!changed.Contains(station)) changed.Add(station);
}
}
if (changed.Count == 0)
{
output.WriteLine("No changes.");
return 0;
}
if (!apply)
{
output.WriteLine($"\nDry run. Pass \"apply\" to write {changed.Count} products.");
return 0;
}
foreach (var product in changed) CultRecordRefs.Validate(product);
cache.Commit(batch =>
{
foreach (var product in changed) batch.Upsert(typeof(FactionProductData), product, cache.RefOf(product).Key);
});
output.WriteLine($"\nWrote {changed.Count} products in Aetheria.cc");
return 0;
}
}