Merge eureka/aetheria-release-nakovalnya-boss-loadout: Zhestokost's demo boss is the Nakovalnya Boss loadout
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
e2e01b2266
3 changed files with 61 additions and 11 deletions
BIN
GameData/Aetheria.cc
(Stored with Git LFS)
BIN
GameData/Aetheria.cc
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
|
@ -37,6 +38,9 @@ public sealed class LoadoutCommitCommandTests : IDisposable
|
|||
return cache;
|
||||
}
|
||||
|
||||
// The loadouts these tests wrote: the shipped catalog already holds the demo boss's, which no command here touches.
|
||||
private static IEnumerable<Loadout> OwnLoadouts(CultCache cache) => cache.GetAll<Loadout>().Where(loadout => loadout.Name != "Nakovalnya Boss");
|
||||
|
||||
private static string Hash(string path) => Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(path)));
|
||||
|
||||
// A hull product and a weapon slot of the shipped catalog that fit together, found the production way: a generated fit,
|
||||
|
|
@ -111,7 +115,7 @@ public sealed class LoadoutCommitCommandTests : IDisposable
|
|||
Assert.StartsWith("Test boss: committed, hull ", output);
|
||||
|
||||
using var cache = Open();
|
||||
var loadout = Assert.Single(cache.GetAll<Loadout>());
|
||||
var loadout = Assert.Single(OwnLoadouts(cache));
|
||||
Assert.Equal(Loadouts.KeyOf("Test boss"), cache.RefOf(loadout).Key);
|
||||
var product = cache.GetAll<FactionProductData>().Single(p => p.Name == fit.hullProduct);
|
||||
Assert.Equal(cache.RefOf(product).Key, loadout.HullProduct.Key);
|
||||
|
|
@ -136,7 +140,7 @@ public sealed class LoadoutCommitCommandTests : IDisposable
|
|||
|
||||
Assert.Equal(0, Run(out _, "Test boss", fit.hullProduct, fit.openSlot, "--replace"));
|
||||
using var cache = Open();
|
||||
var loadout = Assert.Single(cache.GetAll<Loadout>());
|
||||
var loadout = Assert.Single(OwnLoadouts(cache));
|
||||
Assert.False(Assert.Single(loadout.Slots).Product.IsSet());
|
||||
Assert.True(loadout.WeaponGroups == null || loadout.WeaponGroups.Length == 0);
|
||||
}
|
||||
|
|
@ -196,7 +200,7 @@ public sealed class LoadoutCommitCommandTests : IDisposable
|
|||
Assert.DoesNotContain(canary, output);
|
||||
Assert.Equal(before, Hash(CatalogPath));
|
||||
using var after = Open();
|
||||
Assert.Empty(after.GetAll<Loadout>());
|
||||
Assert.Empty(OwnLoadouts(after));
|
||||
}
|
||||
|
||||
// Ruling catalog-is-a-package-stack: a hull product only a package under Mods holds resolves, because names resolve
|
||||
|
|
@ -213,7 +217,7 @@ public sealed class LoadoutCommitCommandTests : IDisposable
|
|||
Assert.StartsWith("Package boss: committed, hull Loadout Commit Skiff (Loadout Commit Skiff Mk I), 0 slots", output);
|
||||
|
||||
using var after = Open();
|
||||
var loadout = Assert.Single(after.GetAll<Loadout>());
|
||||
var loadout = Assert.Single(OwnLoadouts(after));
|
||||
Assert.Equal(ShipModCatalog.HullKey(PackageId), loadout.Hull.Key);
|
||||
Assert.StartsWith("mod-product:" + PackageId + ":", loadout.HullProduct.Key.Value);
|
||||
Assert.Null(after.Get(loadout.Hull));
|
||||
|
|
|
|||
|
|
@ -234,13 +234,58 @@ public sealed partial class RunStartTests
|
|||
}
|
||||
}
|
||||
|
||||
// No generated fallback (rulings boss-gear-is-a-loadout, demo-boss-hull-invented): the shipped catalog authors no boss Loadout,
|
||||
// so Zhestokost has no boss. The refusal happens before anything is drawn.
|
||||
// The demo as shipped (follow-up zhestokost-boss-loadout): Zhestokost's Faction.BossLoadout names the committed Nakovalnya
|
||||
// Loadout, and a demo run reaches a built Nakovalnya carrying exactly that Loadout's cells, nothing drawn from the market.
|
||||
// The gate stays shut while it lives and opens when it dies.
|
||||
[Fact]
|
||||
public void The_shipped_demo_boss_is_Zhestokosts_Nakovalnya_loadout()
|
||||
{
|
||||
var galaxy = DemoGalaxy(1);
|
||||
var antagonist = Zhestokost(galaxy);
|
||||
var loadout = Loadouts.BossOf(_cache, antagonist, out var refusal);
|
||||
Assert.Equal(BossRefusal.None, refusal);
|
||||
Assert.Equal("Nakovalnya Boss", loadout.Name);
|
||||
Assert.Equal("Nakovalnya", _cache.Get(loadout.Hull).Name);
|
||||
Assert.Equal(26, loadout.Slots.Count);
|
||||
|
||||
var hull = _cache.Get(loadout.Hull);
|
||||
Assert.DoesNotContain(hull, LoadoutGenerator.SoldHulls(_cache));
|
||||
|
||||
foreach (var seed in new uint[] { 7, 8, 9 })
|
||||
{
|
||||
var log = new List<string>();
|
||||
var items = LoggingItems(log);
|
||||
var pack = BossZonePack(galaxy, items, galaxy.Exit, seed: seed);
|
||||
Assert.True(pack.BossSpawned, string.Join("; ", log));
|
||||
var boss = Assert.Single(pack.Entities, entity => entity.Boss);
|
||||
Assert.Same(antagonist, _cache.Get(boss.Faction));
|
||||
Assert.Equal("Nakovalnya", HullName(items, boss));
|
||||
// A design-only slot takes the brand its faction can get (ruling loadout-product-optional-context-chooses), so the
|
||||
// cell is compared by position, rotation and design; a slot that pins a product must be built with that product.
|
||||
static string Placed(string cell) => string.Join(",", cell.Split(',').Take(4));
|
||||
Assert.Equal(LoadoutCells(loadout).Select(Placed), Cells(items, boss).Select(Placed));
|
||||
foreach (var pinned in LoadoutCells(loadout).Where(cell => !cell.EndsWith(",-")))
|
||||
Assert.Contains(pinned, Cells(items, boss));
|
||||
// No other ship in the zone flies the boss hull: the market never draws it.
|
||||
Assert.DoesNotContain(pack.Entities, entity => !entity.Boss && entity is ShipPack && HullName(items, entity) == "Nakovalnya");
|
||||
}
|
||||
|
||||
var exit = ZoneOf(galaxy, galaxy.Exit);
|
||||
var nakovalnya = Assert.Single(exit.Entities, entity => entity.IsBoss);
|
||||
Assert.True(RunGoal.BossAlive(exit));
|
||||
Assert.False(RunGoal.ExitOpen(exit));
|
||||
nakovalnya.HeatstrokeDeath.OnNext(Unit.Default);
|
||||
Assert.True(RunGoal.ExitOpen(exit));
|
||||
}
|
||||
|
||||
// No generated fallback (rulings boss-gear-is-a-loadout, demo-boss-hull-invented): a faction that sets no boss Loadout has no
|
||||
// boss. The refusal happens before anything is drawn.
|
||||
[Fact]
|
||||
public void Boss_without_a_boss_loadout_is_refused()
|
||||
{
|
||||
var galaxy = DemoGalaxy(1);
|
||||
var antagonist = Zhestokost(galaxy);
|
||||
antagonist.BossLoadout = default;
|
||||
Assert.Null(Loadouts.BossOf(_cache, antagonist, out var refusal));
|
||||
Assert.Equal(BossRefusal.Unset, refusal);
|
||||
|
||||
|
|
@ -333,9 +378,10 @@ public sealed partial class RunStartTests
|
|||
[Fact]
|
||||
public void Main_galaxy_bosses_are_factions_with_a_boss_loadout()
|
||||
{
|
||||
var factions = _cache.GetAll<Faction>().OrderBy(faction => faction.Name, StringComparer.Ordinal).ToArray();
|
||||
foreach (var faction in factions) faction.BossLoadout = default;
|
||||
Assert.Empty(RunStart.Generate(new MainGalaxy(), Inputs(() => 1)).BossZones);
|
||||
|
||||
var factions = _cache.GetAll<Faction>().OrderBy(faction => faction.Name, StringComparer.Ordinal).ToArray();
|
||||
factions[0].BossLoadout = new CultRecordRef<Loadout>(Loadouts.KeyOf("No Such Loadout"));
|
||||
Assert.Empty(RunStart.Generate(new MainGalaxy(), Inputs(() => 1)).BossZones);
|
||||
|
||||
|
|
@ -431,7 +477,7 @@ public sealed partial class RunStartTests
|
|||
public void Exit_open_only_in_the_exit_zone()
|
||||
{
|
||||
var galaxy = RunStart.Generate(new MainGalaxy(), Inputs(() => 1));
|
||||
// The shipped catalog sets no boss Loadout, so the main galaxy places no boss: give a faction one and a boss zone that is
|
||||
// Give a faction a boss Loadout and a boss zone that is
|
||||
// not the exit, as the generator would for a faction with a boss Loadout. This Loadout pins its hull's brand, so any faction
|
||||
// builds it whatever it can get.
|
||||
var faction = galaxy.Factions.First();
|
||||
|
|
@ -578,7 +624,7 @@ public sealed partial class RunStartTests
|
|||
var galaxy = DemoGalaxy(1);
|
||||
var hull = SoldShipHulls().OrderBy(h => h.Name, StringComparer.Ordinal).First();
|
||||
StoreLoadout(new Loadout { Name = "Aaa Decoy", Hull = _cache.RefOf(hull) });
|
||||
if (dangling) Zhestokost(galaxy).BossLoadout = new CultRecordRef<Loadout>(Loadouts.KeyOf("CANARY-7f3a"));
|
||||
Zhestokost(galaxy).BossLoadout = dangling ? new CultRecordRef<Loadout>(Loadouts.KeyOf("CANARY-7f3a")) : default;
|
||||
var log = new List<string>();
|
||||
var items = LoggingItems(log);
|
||||
var pack = BossZonePack(galaxy, items, galaxy.Exit);
|
||||
|
|
|
|||
Loading…
Reference in a new issue