From a892b5463bac008e95518eca322a6e074e39a0d9 Mon Sep 17 00:00:00 2001 From: Nibu Date: Thu, 5 Nov 2026 11:39:11 +0000 Subject: [PATCH] Wren: the starter's first package, a stand-in light courier in GameData/Mods Written by ship-authoring starter --like Djinni, priced, with an unoffered Aeronautics Unlimited product (so the census counts it sold and no faction fields a box). Tests: it composes into the catalog, its mounts take catalog gear, and the fitted ship moves under thrust. Drop the directory to remove it; replace the model through Blender and Package to make it a real hull. Co-Authored-By: Claude Sonnet 5.5 --- GameData/Mods/aetheria.wren/ship.cc | 3 + GameData/Mods/aetheria.wren/ship.glb | 3 + .../Aetheria.Shared.Tests/WrenPackageTests.cs | 68 +++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 GameData/Mods/aetheria.wren/ship.cc create mode 100644 GameData/Mods/aetheria.wren/ship.glb create mode 100644 tests/Aetheria.Shared.Tests/WrenPackageTests.cs diff --git a/GameData/Mods/aetheria.wren/ship.cc b/GameData/Mods/aetheria.wren/ship.cc new file mode 100644 index 000000000..0b5e70964 --- /dev/null +++ b/GameData/Mods/aetheria.wren/ship.cc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b66dab2993fc365eacaf900ef1405f40b8ac2301977793c61d1c96475743017a +size 10506 diff --git a/GameData/Mods/aetheria.wren/ship.glb b/GameData/Mods/aetheria.wren/ship.glb new file mode 100644 index 000000000..03b70b729 --- /dev/null +++ b/GameData/Mods/aetheria.wren/ship.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98bf1b4a0e19e67568930ca79b9d61529c21c63dd74ee8df3d3f1d419d3a81cc +size 6964 diff --git a/tests/Aetheria.Shared.Tests/WrenPackageTests.cs b/tests/Aetheria.Shared.Tests/WrenPackageTests.cs new file mode 100644 index 000000000..bb95cbb55 --- /dev/null +++ b/tests/Aetheria.Shared.Tests/WrenPackageTests.cs @@ -0,0 +1,68 @@ +using System.Linq; +using CultMath; +using GameCult.Caching; +using Xunit; +using static CultMath.math; +using Random = CultMath.Random; + +// GameData/Mods/aetheria.wren: a first-party package written by `ship-authoring starter --like Djinni`, then priced and given +// an unoffered Aeronautics Unlimited product by `ship-authoring price` and `product`; its model is the starter's boxed +// stand-in. It is the pipeline's proof from a hull package on disk to a ship that flies in the headless simulation: the +// package composes into the catalog, its mounts take catalog gear, and the fitted ship moves under thrust. `fly` cannot +// reach it (an unoffered product is never drawn), so these tests are the flight check until a pinned Loadout exists. +public sealed class WrenPackageTests +{ + private const string Id = "aetheria.wren"; + + private static HullData Wren(CultCache cache) => cache.Get(new CultRecordRef(ShipModCatalog.HullKey(Id))); + + [Fact] + public void TheStarterPackageIsInTheComposedCatalogWithTheStatsOfItsLike() + { + using var cache = RestoredHullsTests.OpenCatalog(); + var hull = Wren(cache); + var like = cache.GetByName("Djinni"); + Assert.NotNull(hull); + Assert.Equal(("Wren", HullType.Ship, like.Mass, like.Durability), (hull.Name, hull.HullType, hull.Mass, hull.Durability)); + Assert.Equal(ShipModCatalog.AuthoringKey(Id), hull.Visual.Key); + Assert.Equal(2, hull.Hardpoints.Count(h => h.Type == HardpointType.Thruster)); + Assert.Equal(2, hull.Hardpoints.Count(h => h.Type == HardpointType.Energy)); + } + + // The starter's product is its maker's but off the market (rulings package-carries-product, off-market-is-unoffered-product): + // the census wants every design sold or placed, and no faction fields a stand-in box. + [Fact] + public void TheStarterIsMadeButNeverOffered() + { + using var cache = RestoredHullsTests.OpenCatalog(); + var hull = Wren(cache); + var product = Assert.Single(cache.GetAll(), p => p.Design.Key.Equals(ShipModCatalog.HullKey(Id))); + Assert.Equal("Aeronautics Unlimited", cache.Get(product.Manufacturer).Name); + Assert.True(product.NotOffered); + Assert.True(hull.Price > 0); + Assert.DoesNotContain(hull, LoadoutGenerator.SoldHulls(cache)); + } + + // Every mount takes a catalog design: the starter's footprints are sized to gear that exists, so it can be fitted out. + [Fact] + public void EveryMountTakesACatalogDesign() + { + using var cache = RestoredHullsTests.OpenCatalog(); + var designs = cache.GetAll().ToArray(); + var bare = Wren(cache).Hardpoints.Where(hardpoint => !designs.Any(hardpoint.Takes)).Select(hardpoint => $"{hardpoint.Transform} ({hardpoint.Type})").ToArray(); + // No shield design fits a 1x1 yet (hardpoint-fit lists Djinni's 4-cell shield with none either); every other mount does. + Assert.Equal(new[] { "internal.shield (Shield)" }, bare); + } + + [Fact] + public void TheStarterFliesUnderThrust() + { + using var cache = RestoredHullsTests.OpenCatalog(); + var ship = RestoredHullsTests.BuildThrustedShip(cache, "Wren"); + var start = ship.Velocity; + ship.MovementDirection = float2(0, 1); + for (var i = 0; i < 10; i++) ship.Zone.Update(1f / 60f); + Assert.NotEqual(start, ship.Velocity); + Assert.True(ship.GetBehavior().Limit > 0f); + } +}