AetherDriveData and AetherDrive, Union(12) (now a retired-do-not-reuse comment, as 26 is),
HardpointType.AetherDrive (last member, nothing shifts), Ship's drive sets, aggregates and
Axis write, the five request-field rows, GameplaySettings Aether multipliers, ShipInstance's
drive particles, SchematicDisplay's RPM display and AetherDriveUi, the undock refusal's drive
clause, the drive prefab and material, and AetherDb's drive roles and brownout row. The
design is kept at parked/aether-drive.
Adds EveryShippedShipHullHasATopSpeed. ARPG.unity still holds stale serialized references to
the removed SchematicDisplay fields and the RPM Display object; the next commit removes them.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The previous commit did not compile AetherDb (missing using) and dropped the
Python name while HardpointType.AetherDrive still existed, which ShipSchemaPinTests
pins. Both repaired; the name leaves with the enum member.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Duel, Arcs, Launcher Angles and Starved Reactor fit and place the Longinus
(thrusters, Core Power or MoveOnPro at (2,4), radiators relaid); the modes start
a Longinus (Settings.asset StartingHullName; the Aether multipliers leave it).
The drive's tests, its Steering Axis probe and its Brownout fixtures go with their
subject; ship_cc.py and the live-game checklist drop the drive and the hull.
The drive itself and the catalog records are deleted in the following commits.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The bind called bpy.path.relpath after setting aetheria.asset_kind and aetheria.id, so a .cc on another drive than the
.blend raised and left the collection half-bound, and an unsaved .blend stored a path relative to the working
directory. The stored path is now computed first: absolute for an unsaved .blend, '//'-relative when relpath succeeds,
absolute when it raises ValueError. Every reader already resolves both through bpy.path.abspath.
smoke_package.py gains the bind cases: unsaved stores absolute and survives two Save As moves, a failing bind leaves
no aetheria.* property, same-drive stores '//'-relative, and with SMOKE_OTHER_DRIVE the final bind crosses drives
before the package passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
smoke_package.py builds a primitive ship from role-tagged objects, creates it --like Djinni, saves a layout with an
internal reactor, packages it and requires validate to pass; then swaps the thruster disc for an empty and requires the
refusal naming the anchor; then restores and packages again so the left-behind package is good. Proof gate 2's
outstanding authoring action is now Package.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every object tagged aetheria.role becomes a ShipAnchor whose Id and ModelNodeId are its aetheria.id (a muzzle's ParentId
is its parent's). The collection, Grease Pencil excluded, exports to ship.glb with custom properties as node extras;
replace_visual writes the anchors, a lone Grease Pencil object's lines are captured, and AetherDb's verdict shows in the
panel. The add-on preference 'Aetheria repo' names the checkout; by default it is the nearest folder above the .cc.
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>
Load reads the raw ship authoring payload and refuses a non-nil retired key 1 (the
pre-S1 embedded hull) naming the old shape, so a stale hull is no longer silently
ignored; ship_cc.py refuses the same file on read and therefore on every save.
A hull naming both a Unity prefab and a visual is refused by one predicate
(ShipAuthoringStore.RequireOneBody), called by Validate and by Compose over every hull
in the derived catalog, shipped ones included; a whitespace-only Prefab is unset. This
replaces the mod-ship-only "cannot name a Unity prefab" check.
The validator also refuses non-finite or negative hardpoint Armor and FiringArc, an
undefined HardpointType or ItemRotation, and an anchor Role outside the eight the
schema documents.
Load wraps MessagePack decode failures with the file path. Python replace_layout
refuses non-boolean layout and hardpoint-footprint cells.
Tests: exact Visual key comparison, HullData.Visual at key 32, a save refreshing
stored_at, plus one case per new refusal. HullData.Visual is now [Inspectable] like
Prefab (Unity editor only; not compiled here).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A mod ship file now holds a HullData at mod-hull:<id> and the ShipAuthoring
visual package at mod-ship:<id>. HullData.Visual (key 32) is the typed ref
between them; ShipAuthoring.Hull (key 1) is retired.
- One joint validator over (hull, visual) is the only semantic check; Load
owns the file structure (one hull, one visual, deterministic keys).
- Compose copies the mod store's records verbatim, so the derived catalog
holds one hull per mod ship.
- ship_cc.py edits the hull record's slots directly; the body[HULL_SLOT]
indirection is gone.
- The package fixture is generated from ShipAuthoringTests.Fixture() (glTF
scene included) and exported when AETHERIA_SHIP_FIXTURE_DIR is set.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A pass of 137 hand-written mutations over ShipAuthoring.cs, ShipModCatalog.cs
and ship_cc.py, run against the tests from the last three commits, left six
surviving or unrunnable. Each is resolved:
- Validator: the hull Width/Height < 1 guards and the opacity IsInfinity
guard could not change any outcome (an empty grid has no occupied cell;
+Infinity exceeds 1 and -Infinity is below 0). Deleted.
- Two accepted shapes had no test: a hardpoint footprint with empty cells
beside occupied ones, and an empty ParentId meaning no parent. Added.
- The GLB header length was pinned only from above. Added the shorter case.
- Compose's working-directory cleanup was invisible because the assertion
listed files, not directory entries. It now lists both.
- ReadPackage runs the catalog's item validation on the hull (the derived
catalog must reopen); that call now has a test.
- Python: a single non-ship record, a non-array payload long enough to fill
every slot, and a hardpoint-only change to the layout revision each had a
surviving mutation. Tests added.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ship_cc.py edited a ShipAuthoring record by bare slot numbers and hardpoint
positions (hp[3], hp[7:], raw[0..6]), and __init__.py copied the
HardpointType and ItemRotation enums by position. Nothing tested either
file and nothing tied the numbers to the C# [Key] attributes that own them.
- ship_cc.py names its schema facts once: the four record slots (each
commented with the C# member it mirrors), HARDPOINT_MEMBERS (HardpointData
member names in slot order), and the two enum name tuples. Hardpoint rows
are built and read through encode_hardpoint/decode_hardpoint; replace_layout
and the Blender operators no longer index a row by literal. __init__.py
builds its enum items from the tuples. The rotation label
"Counterclockwise" becomes "CounterClockwise", matching the C# member.
- tools/blender/tests/test_ship_cc.py (30 unittest cases, bpy-free, needs
CultLib's cultcache-py and msgpack): read, replace_lines, read_layout,
replace_layout (untouched slots and later-schema hardpoint slots preserved
by mount id, schema catalog preserved, every refusal leaves the file
byte-identical), the row codec, and capture_grease_pencil against stand-in
objects.
- ShipSchemaPinTests (C#, 7 cases) reads ship_cc.py and fails when a slot
constant, a HARDPOINT_MEMBERS name or position, or an enum name/value
disagrees with the C# types, and when a serialized ship no longer has the
nested array shapes the Python indexes (schematic [[w, h, cells]], cells
x-major, hardpoint transform and footprint positions).
Reading the slots from the file's schema catalog instead was rejected: the
catalog lists only document members, and Hull, Shape and HardpointData are
nested types it does not describe.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>