Holding heading after a state stops piloting (SteeringTests, and a Minion that
loses its target), Combat leading a moving target, MoveTo's Aim and Turn in the
low-delta regime, the behind-the-hull threshold, the Turn clamp, and the hold
branch clearing a stale MovementDirection.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Stryker survivors on the first run: the low-delta branches of Accelerate had no behavioural test,
the velocity error was only exercised from rest, and astern was asserted by magnitude only.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Ship.Update reads Turn and feeds the rotation thrusters and aether drives; it no longer reads the
look direction or snaps Direction toward it. Steering.Toward is the one law from heading to demand
(a heading astern now turns at full demand). HelmInput owns the face-the-aim state. Accelerate,
Combat and MoveTo write Turn through Steering.Toward. Entity.LookDirection is renamed Aim in the
simulation, the Unity readers and the tests. Unity player input follows in the next commit.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Armour repair is priced at fractional wear (a fraction of a cell on every cell, and a thousandth of one cell), and
gear repair is priced exactly when RepairFraction exceeds SellFraction.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An armour price one credit low survived: ceil absorbs it at the fixture's even hull price. The armour test now runs
the same wear patterns at five hull prices.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A price and durability sweep (1..3000 by 20, three fraction pairs) and the 360 at 7 of 20 case assert repairing then
selling never nets a credit. Armour tests wear several cells, partial and full strip, different cell values, and
check every cell is restored and the cost is the worn share of the hull price. A gear test pins the repair share at
odd and even prices.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RepairCost adds the worn share of hull armour priced as that share of the hull's price, and TryRepair restores it. The per-unit-of-wear charge is the larger of RepairFraction and SellFraction. TryBuy checks credits with the same boundary as TryRepair and charges after delivery. A 100-unit ore sale pins the commodity sell scale.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Kills the two survivors Stryker left in StationServices.cs: Any() to All() over the station's bays, and Concat() to Except() over the docking bays.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Kills the two survivors Stryker left in StationServices.cs: Any() to All() over the station's bays, and Concat() to Except() over the docking bays.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RunSave.Capture takes the credits; a scenario may set the starting amount, defaulting to the gameplay settings'.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Settings gain SellFraction, RepairFraction and StartingCredits. A sale pays the price scaled by condition and moves the
item into a station bay or refuses with everything unchanged; a repair restores every durability and charges the wear,
or refuses when short.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mining owns belts, chunks and target picking; scenarios owns admission and run
staging; master owns ships and fire control (rule scenarios-cut.md S4).
- AetheriaStores.CatalogTypes: nine-type union, master's list plus FieldKindData, no Scenario.
- tools/AetherDb/Program.cs: both cases (ship-authoring, field-kinds), one usage string.
- Zone.cs: master's MineAsteroid dropped (mining deleted its inputs); scenarios' SettleBelts and its three calls deleted.
- RunStartTests: the two belt-task tests deleted (they read belt.Transforms and MineAsteroid).
- FireControlCut124Tests: master's strict Exact assertions; mining's CompatibleDrift allowance and its comment dropped.
- GameData/Aetheria.cc: master's pointer 70a7b0a9. This commit lacks the Asteroid field kind in the catalog, so
TheShippedCatalogHasAPickableFieldKind fails here by design; the next commit rebuilds it.
Builds: this commit builds (verified at the end with commit 2); the catalog pin test fails at this commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A second density-equal belt size and reach, held-target presses and the reticle at three headings,
each ceiling within about 10% of the measured count; the shared ceiling drops from 1500 to 700.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pins two Stryker survivors: Entity.Order's field comparison and DistanceKey.Bound's forward bound.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Launch cleared the run store before generating, and a generation or staging failure only reached the log: the
Generating Galaxy dialog stayed up, StartGame threw, and the saved run was already gone.
RunSave.Replace runs a start with the saved run still stored: when it returns a staged run the saved run's records go,
and when it returns null or throws, only the records it wrote (the arena's orbits and bodies) go. StartGame's
new-run branch is StartScenario, chosen by PendingScenario rather than by the absence of a SavedGame; it mints into an
empty ledger, and a failed start clears CurrentGalaxy (so quitting saves nothing) and refuses on the main menu with
every failure. Launch no longer clears; a galaxy that throws is refused on the title menu. The menu offers Continue
whenever no run is live, which replaces MainMenu.InGame (cut, with its prefab value and scene override), so a
failed start can resume the saved run in place.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ScenarioStage.Generated picked from ItemManager.Random, which is clock-seeded per process and advances in play, so a
relaunch of Djinni Shakedown or Long Haul in one session gave the player a different fit; every branded lot's
workmanship and role fills came from the same clock stream, so arena gear and explicit fits varied in quality too.
Galaxy now keeps the seed it was generated at, and RunStart.GenerateArena and Stage each reseed the item manager's
random from it, one stream per step, so what runs between them cannot shift either.
SeedRules relaunches every fixed-seed scenario around a clock-free Main Galaxy launch and a scrambled item random,
and compares the galaxy (links, factions, homes, exit too), the arena down to each item's maker and workmanship, and
the staged fits.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The vendored MIConvexHull (NIH, ported from double to float) returned triangulations that left whole sets of
interior zones with no cell at all (prelude seed 103: 22 of 64 zones; main seed 4247651650: 182 of 196). Those
zones had no link, Distance lacked them, and the constructor threw KeyNotFoundException placing the antagonist
home (prelude) or the entrance (main). Galaxy was its only consumer; Delaunay.Edges (Bowyer-Watson, double) replaces
it, and the 3,896 vendored lines go.
The three RestoredHullsTests loops that skipped seeds whose galaxy threw now require every seed to build, and the
stale comment that Galaxy mutates Faction.InfluenceDistance goes with them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Scenario is an abstract C# class compiled with the game: Generate picks the galaxy through GalaxyStage (Main or
Prelude, at the scenario's seed, the background noise drawn from the same seed), Stage places the run's first
entities through ScenarioStage (Fit, Bare, Generated, Preset, Player, Place, Cargo), which collects named failures
and admits nothing. RunStart generates the galaxy, the arena under Ambient and admits everything through Zone.Admit,
all or nothing. The aetheria.scenario record, ScenarioShip, ScenarioEntity, RunStart.Check and the null-scenario
branch are cut.
New Game opens a submenu: Tutorial Galaxy and Main Galaxy, then (editor and development builds) Djinni Shakedown,
Starved Reactor, Arcs, Duel, Launcher Angles and Long Haul, each with its brief. MainMenu.Launch is the one way into a
new run; ActionGameManager.PendingScenario carries the scenario to StartGame once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IsTutorial duplicated Galaxy.IsPrelude; its readers read CurrentGalaxy.IsPrelude, and RunSave.Capture writes
SavedGame.IsTutorial from galaxy.IsPrelude with no isTutorial argument. TutorialPassed was never written and its only
reader was New Game's unreachable main-galaxy branch, which goes with it (S2 brings the main galaxy back as a
scenario). spawnturret's two identical generator branches collapse to one. Old player.cc files still load.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MODEL_ASSET_SLOT, ANCHORS_SLOT and ANCHOR_MEMBERS sit beside the other slot constants and ShipSchemaPinTests binds them
to ShipAuthoring's and ShipAnchor's [Key]s. replace_visual carries an anchor's slots past Order by Id, refuses a blank
or duplicate id, a wrong-length row and a changed ship id, and leaves the hull record and the lines alone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ShipAuthoringStore.HullLike copies a reference hull through its MessagePack round trip and resets Name, Shape,
Hardpoints, Prefab, Schematic and Visual. 'create ... --like <name>' opens the shipped catalog read-only and refuses an
unknown or non-ship hull. 'validate' runs ReadPackage when ModelAsset names an existing file and says which it judged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ReadNodeIds is the one GLB parse and now records, per aetheria.id node, whether it carries a mesh. Bind refuses a
thruster-emitter or radiator-mesh anchor whose node has none, naming the ship and anchor (ruling
thrusters-radiators-are-meshes), so Compose and validate refuse such a package before the catalog sees it. AetherDb
'ship-authoring validate' judges the GLB when it exists (next commit).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three tests in MiningIndexTests, no production change. RegionsStayNearTheSearchArc bounds the share
of a small search's regions that lie past reach on both sides (window widened, nearest loosened);
VisibilityBoundPrunesADimBelt bounds rocks examined against rocks in reach in a half-lit belt of
largest rocks; ARockExactlyAtReachIsFound searches from 1-300 units with reach set to a rock's exact
distance, a third of them radially outward from a belt's outermost rock. Each threshold's unmutated
and mutant values are in its comment.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Soul's scenarios-adopt probe S4 as a committed test: on generated ships,
stations and turrets every weapon, thruster and radiator holds a non-null
Hardpoint containing all its cells, and Tool gear and the hull hold none.
It pins the input the muzzle, arc, thruster-emitter and radiator readers
dereference (finding headless-reader-input).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The one conflict is EntityInstance's SoundBank block, which this branch had
moved onto item.Hardpoint and ship-mounts deleted: it stays deleted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
BeltTargets drops its own reach skip (TargetRegion.CanHold owns it) and an
empty-belt guard AsteroidBelt already makes unreachable. Tests add an
off-centre sun, whole-belt searches across the antipode, cost after time
passes, a late short step that re-keys nothing, and an entity search off the
origin.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Soul's scenarios batch-4 probe P30 lived only in a session scratchpad. Its
fixtures are now tests: an L gun in an L hardpoint whose origin lies outside
it, an edge gun whose origin lies in a neighbouring radiator hardpoint,
TryFindSpace finding what ItemFits accepts (unrotated and turned), reload into
the same hardpoint, and Tool gear holding none. Three batch-4 minors are
pinned too: IsFilledBy against a cell oracle written in the test, the last-row
placement search, and billing independent of heater equip order.
The file is a part of RunStartTests, so it shares that fixture's scratch-hull
and catalog helpers instead of a second copy of them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Kills the Stryker survivor that armed every weapon with any muzzle on the ship
(the && in the per-weapon muzzle check turned into ||): a second weapon mount
with no muzzle is refused even when the first has one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The one conflict is AetheriaStores.CatalogTypes: keep ShipAuthoring from the
ships lane and Scenario from the scenarios lane.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ShipAuthoringStore.Validate now alone decides which hardpoints carry anchors
and which role each plays. A thruster, radiator, energy, ballistic or launcher
hardpoint needs an anchor of its mount id with role thruster-emitter,
radiator-mesh or the new weapon-mount; every other hardpoint is internal and
no anchor may take its id. A mount-role anchor must be a hardpoint's mount, a
muzzle must hang off a weapon mount, and each weapon needs a muzzle; these
checks move out of ShipModPlan.Build, which now only maps a validated pair to
slots and throws nothing itself. 'articulation' stays in the vocabulary for
pivots and is never a mount.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The array's only reader was the dead SoundBank block in EntityInstance, which
looked up a hardpoint transform and did nothing with it. ShipModPlan.Equipment,
ShipModShips' assignment, the preview check, the FBX builder's HP.Equipment
case and its smoke assertion all fed that one dead reader. An HP.Equipment node
is now refused by the builder as an unknown hardpoint kind.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- AFirstLoadKindIsSavedFromACleanStore: FieldKinds.Ensure's kind is saved
when the store was clean at load (ZoneData.cs:162).
- SetTargetRefusesALitChunkBeyondSight: a lit rock too far to see is
refused (Entity.cs:345).
- TheChunkQueryMatchesBruteForce: 3000 random queries, off-origin, two
belts, across the outer skip edge (Soul F7).
- ThePingArmIsUnchangedByTheGainExtraction: Soul's ping sweep, golden
recorded at cb4a1dbd; kills the Sensor.cs:168 and :189 mutants.
- TheShippedCatalogHasAPickableFieldKind (Soul F2).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Weapon.CanMine is the one mining predicate; LockWeapon overrides it to
false. Entity.VisibleChunksInReach counts only weapons that can mine.
Tests: LaunchersDoNotCountTowardChunkReach (a launcher-only ship is offered
no rock; a gun of 200 beside a launcher of 400 is offered the near rock
only) and AnOfflineWeaponAddsNoReach (Soul F8: reach reads Active, not
Enabled).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>