Faction.ManufacturerOnly (key 19): one territory rule for both galaxies; Pirates allegiance re-runnable

Galaxy.HoldsTerritory is read by the main draw (filtered before the take) and by
ResolveCast for every prelude cast slot. pirates-faction on an existing Pirates
record tops up allegiance under its key instead of returning early; the first
apply and the top-up share AllegianceGaps. The snapshot constants in
MakerOnlyFactionTests are placeholders: this commit's tests do not pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-17 05:51:28 +02:00
commit 570834121e
5 changed files with 293 additions and 10 deletions

View file

@ -63,6 +63,12 @@ public class Faction
// The faction's rules of engagement; null fights as FactionDoctrine.Default.
[Inspectable, JsonProperty("doctrine"), Key(17)]
public FactionDoctrine Doctrine;
// Key 18 is reserved for Livery (follow-up livery-key-collision).
// A manufacturer-only faction sells through other factions' allegiance and never holds territory (ruling minor-power-brands).
[Inspectable, JsonProperty("manufacturerOnly"), Key(19)]
public bool ManufacturerOnly;
}
[CultDocument("aetheria.namefile", "1"), Inspectable, MessagePackObject]

View file

@ -106,7 +106,7 @@ public class Galaxy
IsPrelude = false;
Background = background;
Log = log;
var factions = cache.GetAll<Faction>();
var factions = cache.GetAll<Faction>().Where(HoldsTerritory);
Seed = seed == 0 ? (uint) (DateTime.Now.Ticks % uint.MaxValue) : seed;
var random = new Random(Seed);
Factions = factions.OrderBy(x => random.NextFloat()).Take(settings.MegaCount).ToArray();
@ -143,8 +143,15 @@ public class Galaxy
}
// A cast member of a prelude galaxy, by name. A name the catalog does not hold refuses the galaxy, naming the field.
private Faction ResolveCast(string field, string name) =>
ResolveFaction(name) ?? throw new InvalidOperationException($"cast faction {name} ({field}) is not in the catalog");
private Faction ResolveCast(string field, string name)
{
var faction = ResolveFaction(name) ?? throw new InvalidOperationException($"cast faction {name} ({field}) is not in the catalog");
return HoldsTerritory(faction) ? faction
: throw new InvalidOperationException($"cast faction {name} ({field}) is manufacturer-only and holds no territory");
}
// The one territory rule: a manufacturer-only faction sells through allegiance and is placed in no galaxy.
private static bool HoldsTerritory(Faction faction) => !faction.ManufacturerOnly;
// Gives a prelude galaxy its gate: the antagonist's home is the boss zone and the exit. The only writer of Exit
// besides the constructors.