Commit graph

2,409 commits

Author SHA1 Message Date
Meta Vens
4f6bbbb8da Ship S2 smoke fixture: the node index test names the weapon nodes
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 10:54:02 +02:00
Meta Vens
5b58b947bd Ship S2 smoke fixture: the weapon mount takes the hull other cell instead of moving the thruster off the schematic
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 10:53:26 +02:00
Meta Vens
54dcf5214c Ship S2 smoke fixture: the generated GLB carries a tetrahedron mesh on the map, collider and thruster anchors, plus a weapon mount and muzzle
The fixture package now has real vertices (BIN chunk written by the generator, no committed bytes), an energy hardpoint "gun" with a
muzzle anchor, and the thruster moved outside the schematic, so ShipModShips.Assemble accepts it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 10:52:47 +02:00
Meta Vens
34319b0df1 Ship S2 fixes: RequireDesigns refuses nulls explicitly, and a failing Compose falls back to the shipped catalog
- RequireDesigns names the null (entity, item, zone record, lot, lot ledger) instead of catching NullReferenceException.
- ResolveCatalog reports a Compose-level failure as an exclusion covering all mods and boots the shipped catalog.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 10:06:16 +02:00
Meta Vens
8f8b72be3c Ship S2 fixes: the edge-of-range hardpoint test re-roles its anchor with its type
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 08:34:54 +02:00
Meta Vens
5ed3b759c4 Ship S2 fixes: the RunSave designs fixture resolves its gear ref
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 08:31:24 +02:00
Meta Vens
54917bb39d Ship S2 fixes: structural anchors are not mounts, mod presets refused, one bad mod never bricks the boot
- Validate: a hardpoint's anchor role is fixed by its type (thruster-emitter, radiator-mesh, else articulation); ShipModPlan's
  duplicate role check is deleted.
- Loadouts.Commit refuses a preset naming a mod design (mod-hull:/mod-ship:), so the shipped catalog never references a mod.
- Compose quarantines each package: a bad package is excluded and named, a collision excludes every package involved; the
  game boots on the rest (or the shipped catalog) and the main menu lists the exclusions.
- RequireDesigns refuses a save with a null item or lot instead of throwing NullReferenceException.
- Tests: per structural role, muzzle id ties, every slot/zone/lot in RequireDesigns, listing order, exclusion cases.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 08:30:52 +02:00
Meta Vens
7eebd21d29 Ship S2: commit the .meta files for ShipModShips and ShipModPlan
Unity generated them on import; without them the two scripts' GUIDs are
unpinned and differ per checkout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 08:20:33 +02:00
Meta Vens
81abd8ef2b Ship authoring doc: the game reads mod ships (S2)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 07:53:38 +02:00
Meta Vens
86318ec014 Ship S2 tests: muzzle order against ids, every weapon type, PackageOf validates
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 06:46:17 +02:00
Meta Vens
460bb85ed0 Ship S2: the game composes and loads mod ships
Boot opens the derived catalog when GameData/Mods holds packages
(ShipModCatalog.ResolveCatalog), preloads each hull's GLB into an assembled
inactive prototype (ShipModShips), and LoadEntity clones it when the hull names
a Visual. ShipModPlan decides which anchor fills which ShipInstance slot; a
ShipModTemplate prefab carries the shared effects. Continue refuses a run that
names a design the catalog no longer holds (RunSave.RequireDesigns).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 06:43:52 +02:00
Meta Vens
f470c1b775 Ship S1 fixes: refuse the retired embedded hull, one body per hull, closed validator gaps
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>
2026-09-30 06:18:20 +02:00
Meta Vens
d1648800ab Ship authoring doc: one hull record, a hull names its visual
Owner, shared-path and compose paragraphs describe the S1 shape: a ship
file holds a HullData and the ShipAuthoring visual it names through
HullData.Visual, and the derived catalog holds one hull per mod ship.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 03:48:47 +02:00
Meta Vens
1702e0549f Ship S1: one hull record, and a hull names its visual
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>
2026-09-30 03:06:43 +02:00
Meta Vens
de1b5b8fa2 Ships map: S0 Unity compile and smoke passed; fixture package owed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 02:57:08 +02:00
Meta Vens
01e9db1c27 Ship S0: correct the authoring doc, record the merge status in the map
Applies corrections 1-4 of docs/moddable-ships-cut.md B.1 to
docs/moddable-ship-authoring.md: label the Quiet probe numbers as unrecorded,
name the double-held mod hull in the derived catalog, name the ShipAuthoring
catalog-routing invariant, and scope "same semantic validator" to C# callers.
The map's status header gains the S0 result (615 tests on the merge), the
S-Soul status, the corrected TradeMenu.cs path, and anchors re-taken against
the merged tree.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 02:26:50 +02:00
Meta Vens
18e12d81ce Merge master (fire control 12) into codex/moddable-ships 2026-09-30 02:21:09 +02:00
Meta Vens
fe38f28865 Close the gaps a mutation pass found in the new ship tests (S-Soul F1-F3)
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>
2026-09-30 02:14:10 +02:00
Meta Vens
65c6349537 Map the scenario cut: authored arenas launched from the main menu
Imagination pass 1 against dcd7bbc5, designed from the operator's own
words (quoted in the map). Cuts 0-4; four operator questions open
(Q1 arena in a real run, Q2 who records results, Q3 test-only designs,
Q4 which ad hoc affordances retire).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 02:11:09 +02:00
Meta Vens
dcd7bbc5f6 Merge fire control (codex/fire-control-12) into master
Fire-control batches 1-8 with Soul passes, verified headless (Aetheria
Shared tests). Play smoke 2026-09-30: compile clean, steps 3-6 played
(pass/partial), steps 7-20 and fire-control checks A-F unplayed. The
operator preferred to merge now; Self ruled merge, with the unplayed
checks carried as open behaviours to verify through authored main-menu
scenarios (new Eureka cut). Report: docs/merge-to-master-checklist.md
section 8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 01:32:19 +02:00
Meta Vens
d3075eb06d Record the play smoke stop, operator direction and stale checklist expectations
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 01:15:06 +02:00
Meta Vens
f935c29b4b Fix console text input and route give through ship commissioning
- ConsoleView appended the control characters text input delivers (\b, \r),
  so Backspace inserted a glyph and Enter's \r got every command refused
  since d503f076 made the parser refuse instead of strip. Control characters
  are no longer text; Backspace deletes like Delete.
- give reports an unknown name or a full cargo bay. A ship hull is refused
  unless docked, and when docked is commissioned through
  ActionGameManager.CommissionShip, the primitive TradeMenu.Buy now uses: a
  bare player ship moored at the docked entity, no docking bay. Non-ship
  hulls are refused.
- Record play smoke results for steps 3-6 and the docking bay finding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 01:04:11 +02:00
Meta Vens
c6430c93e4 Make AetherDb the agent's data tool in project doctrine
Missing catalog or save capabilities are added to AetherDb as commands,
not written as scratch projects, loose scripts or byte readers. Records
the serializer options and the CultLib pin gate an agent hits on first use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 00:08:34 +02:00
Meta Vens
1f8c0ccd6d Give the Blender side tests and pin its hand-copied schema to C# (S-Soul F3)
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>
2026-09-29 23:55:46 +02:00
Meta Vens
1987009ba1 Pin Compose, ReadPackage and ReadNodeIds; refuse name collisions (S-Soul F2, F6)
Compose, ReadPackage and ReadNodeIds had no test. ShipModCatalogTests now
covers the derived catalog, the atomic replace of an existing one, every
failure path leaving the previous derived file byte-identical, the unsafe
source/output refusals, package binding (directory name, GLB existence and
extension, anchor node presence), and the GLB reader (each header guard, the
JSON chunk boundary, node index accounting, duplicate ids).

Writing them exposed three defects in Compose, fixed here:

- F6: AetheriaStores.CatalogTypes routes ShipAuthoring to the shipped
  catalog, and CultCache indexes names per concrete type with last writer
  winning while GetByName over a base type throws on a cross-subtype match.
  Compose checked keys only, so a mod hull named like a shipped item, or like
  another mod's hull, silently shadowed it or broke lookups. It now refuses
  a hull name matching any catalog ItemData and a ship ID matching an
  existing ShipAuthoring, naming the ship.
- The backing store writes a .lock file beside every file it opens, so each
  Compose left <output>.<guid>.tmp.lock behind, success or failure, once per
  boot. The working copy now lives in its own directory that is deleted whole.
- Deleted: the duplicate-ID check (ReadPackage already pins ID to directory
  name, so it could not fire) and ReadPackage's containment StartsWith
  (Validate already refuses rooted and `..` model paths on the same Read).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:47:13 +02:00
Meta Vens
5df72ba363 Pin every refusal in ShipAuthoringStore.Validate and Read (S-Soul F1)
Soul's mutation pass on 375d6bd4 found 8 of 9 validator mutations
surviving, including the `..` model-path guard and the prefab refusal. The
validator had five hand-picked cases. ShipValidationTests now carries one
case per guard: each starts from the valid fixture, breaks exactly one
thing, and names the message that guard alone produces. It also pins the ID
alphabet at both ends of each range, the accepted model-asset spellings
beside the refused ones, the range edges of point attributes, an anchor
naming its hardpoint as parent, and Read's exactly-one-record and
validate-on-load behaviour.

ShipAuthoringTests.Fixture becomes internal so the new file shares it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:46:44 +02:00
Meta Vens
97edf48cec Add the debug info panel and the shield panel test rig
ARPG.unity gains Debug Info Panel and Debug Info Text, which
ActionGameManager.DebugInfoText drives as the fire-control debug HUD.
FieldShieldTest.unity gains the ShieldInterceptor, ShieldEnvelope and
Panel prototype rig from shield-panel Cut 4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:38:54 +02:00
Meta Vens
e0a802084e Use the generated accessor for Cycle Target Item
Regenerate AetheriaInput.cs from the committed Aetheria.inputactions so the
wrapper carries Cycle Target Item, and bind it through Input.Player like the
other target actions. The by-name FindAction lookup was a stopgap from when
the generator could not be run outside the editor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:38:48 +02:00
Meta Vens
113164fadb Record fire control batches 5-8 landing and their follow-ups
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 22:24:39 +02:00
Meta Vens
06001b57a8 Merge fire control fix batches 5-8: fused weapons follow player intent
One Solve decision feeds Fire, Inspect and Refuses. Literal arming distance
on every detonation path; no-lock rounds of any fuse stop at the first hull;
a round fired at a target that leaves does not burst; refusal is free,
cooldown included; AI decides per weapon and never fires a refused weapon;
the HUD shows the outcome. Soul passes 5-7; batch 8 closes their follow-ups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 22:21:12 +02:00
Meta Vens
7f317bd332 Freeze a guided round that waits on its verdict and place a positionless hit at its target
After its fade a round kept integrating Velocity through the BindingLifetime wait, then a Hit (which carries
no burst point) played HitEffect at the drifted position. The wait now holds still; a hit without a point
plays the effect at the round's target position, and is skipped when the round never knew one. OnEnable resets
BindingLifetime so a pooled split child, which is never bound, does not inherit the last round's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 22:15:53 +02:00
Meta Vens
742cb23f5b Gate a charged weapon's Activate on CanFire, and start a burst's cooldown with the tick it flew in
A released charge has a burst pending and no cooldown until its first round, so a second Activate in that
window started a second charge; Activate now asks CanFire. The round that starts a burst's cooldown does so
after the tick's decrement, which made every cooldown one tick long; it now charges that tick to the cooldown.
Pins both, and that a burst triggered after a fired one is not ready before its first round.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 22:15:51 +02:00
Meta Vens
819b282346 Pin that Combat picks no group whose weapon cannot reach the target
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:54:36 +02:00
Meta Vens
c0fb26fca1 Pin that a triggered burst is not ready before its first round
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:39:18 +02:00
Meta Vens
1c5872e7a8 Give the stance-test gun a burst and a cooldown
The default InstantWeaponData has Count 0, so the fixture gun never had a round to fire.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:35:53 +02:00
Meta Vens
c4b628fad7 Resolve the stance-test gun before triggering it
A burst is sized from stats the first Execute resolves, so the untouched fixture gun fired nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:32:31 +02:00
Meta Vens
020a42fcb6 Execute the weapon directly in the stance tests
The stance fixture has a shooter the zone cannot tick; the weapon runs on its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:31:38 +02:00
Meta Vens
7c1b1cd3d8 Tick the zone before reading the cooldown in the stance tests
BurstCount is set by the first Execute, so a trigger pulled with no tick has no burst pending. The
tests now run one tick and read whether the cooldown started.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:27:28 +02:00
Meta Vens
0e73bbac72 Read the pending burst, not Progress, as the mark that Trigger ran; sound the charged control by its own event
The cooldown now starts at a burst's first fired round, so Progress no longer moves at the trigger.
IffAndCombatTests read CanFire (false while a burst is pending) instead, and the charged-weapon
control uses the charge-start event name the sound bank resolves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:23:04 +02:00
Meta Vens
136fddf15a Start the cooldown at a burst's first unrefused round; refused charged weapons never charge
A burst whose every round is refused fires nothing and leaves no cooldown: the cooldown moves from
Trigger to the first round Execute does not refuse, and CanFire holds a triggered burst that has not
yet fired. ChargedWeapon.Activate asks FireControl before it starts charging, so a refused weapon pays
no charge heat or audio. InReach is deleted: Designated carries the one range test and the lock gate,
and Combat reads it. GuidedProjectile answers Resolve while alive and keeps its binding through the
shot's flight time, so a faded round still shows the simulation's detonation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:16:52 +02:00
Meta Vens
76e78db4b2 Record that a refused round starts no cooldown
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:52:21 +02:00
Meta Vens
d00d158007 Pin the flight distance of a pushed-out no-lock round
The mutation pass on the fuse fixes left one survivor: a no-lock round whose
aim ends inside the blast radius (a designated target out of arc) flew only to
its aimed point instead of the arming distance, and nothing noticed. Its arrival
is now pinned to the arming distance, beside the targeted case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:42:30 +02:00
Meta Vens
57c2560fc0 Fuse fixes: arming holds for every burst, no-lock rounds stop on the first hull, settle before user code
Operator ruling 2026-09-30: a no-lock round of any fuse stops at the first hull
on its aim line and detonates there (bomb fishing still bursts at max range on
a clear line). CommitBurst no longer special-cases Contact.

Soul's findings on 14f66634..7e620ff9:
- Arming (HIGH): a contact or delayed blast on a hull nearer the muzzle than the
  blast radius detonated at the hull. Armed is now the one owner of the arming
  rule; Solve plans its burst point through it and ApplyBlastHit pushes the hull
  point through it.
- A refused beam never starts (ConstantWeapon.Activate asks Refuses), so
  OnStartFiring no longer flashes visuals and audio for a beam that fires nothing.
- Step removes a shot from the queue before Apply runs damage observers, so a
  throwing observer cannot make the blast repeat every tick.
- AgentFires consults Solve for a fused weapon: a refused weapon is never fired
  and Combat's group pick no longer counts its damage.
- SingleAmmoBurst pays at its first unrefused round in Execute, judged with the
  Range that round flies with, instead of at the trigger on the previous tick's.
- Inspect reads Designated from Solve (FireSolution.Designated) instead of
  recomputing visible, in-range and locked; FireControlDiagnostic drops those
  three fields and the debug HUD shows the one. Combat's group range test is
  FireControl.InReach, the range half of Designated (a LockWeapon must be
  activated to acquire its lock, so Combat cannot ask Designated itself).

Tests pin each rule, plus the two surviving gaps: a pushed-out targeted round's
arrival at the arming distance, and the AgentMinHitProbability boundary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:31:59 +02:00
Meta Vens
63a2fcdaa4 Guided projectile: only the simulation's detonation shows an explosion
The overshoot branch of GuidedProjectile.Update (out of range, or flown past
its target) instantiated HitEffect whatever the simulation had decided. A round
the simulation never detonated could therefore explode on screen. The branch now
fades the round out; Resolve stays the one place a HitEffect is shown for a
detonation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:20:06 +02:00
Meta Vens
d3be00c23a Record the all-fuses first-hull ruling and the batch 5 Soul pass
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:10:06 +02:00
Meta Vens
7e620ff9f2 Run the refused-round cost test through Execute as well as the trigger
The trigger's own refusal check stopped a short-Range round before Execute
ever reached its refusal, so wear, heat, visibility and sound left ahead of
Execute's `continue` survived every mutation. The cost theory now also
authorises a burst directly, the way a trigger that passed leaves it, and the
separate ammo-only test folds into it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 18:51:57 +02:00
Meta Vens
8c63fbe50e Guided projectile: binding lives and dies with the projectile; no explosion without a detonation
Unity-only, compile-checked by reading.

* F5: GuidedProjectile.Binding is one owner: a new binding disposes the one it
  replaces, Kill disposes it, and OnDestroy does too, so a projectile torn down
  with its scene cannot leave an observer on the zone's subjects to touch a
  destroyed transform.
* F6: Resolve takes whether the simulation detonated. A miss (a contact or
  delayed round that missed, a target that left) fades out with no explosion.
* F7: BurstAt resets the previous target position and target velocity, so the
  retarget jump does not feed the guidance a bogus target velocity.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 18:37:02 +02:00
Meta Vens
a193853d34 Fuse batch 5: agents fire per weapon, Solve owns the burst, refusal is free, arming is the blast radius
Operator rulings 2026-09-30.

* Combat and TurretController ask FireControl.AgentFires for each weapon. A
  fused weapon fires whenever its target is designated (Designated, the one
  visible / in-range / locked predicate PFire already used), whatever the arc;
  an agent never fires one at nothing. A direct-hit weapon keeps the
  AgentMinHitProbability threshold. The group's first weapon no longer speaks
  for the group, and the turret's own range test is gone.
* FireControl.Solve is the one decision about what a round does, extracted
  from Fire: Direct, Burst at a reach, or Refused. Fire, Inspect and the
  weapons' refusal check all read it. Inspect gains Outcome and BurstReach and
  the debug HUD prints them for fused weapons.
* A refused round costs nothing. InstantWeapon asks before its ammo and energy
  spend (at the trigger and at each round), ConstantWeapon folds the refusal
  into the gate its power request and Execute already share, so a refused beam
  draws no power, wear, heat or visibility and stops.
* The arming distance is BlastRadius, measured from the shooter's position and
  blind to hull size. HullAhead's exit, ArmingDistance and
  PendingShot.ArmingDistance are deleted; HullAhead survives as HullEntry for
  the contact fuse. A round fired at a valid target inside the radius is pushed
  out along its line but keeps its target, so a departed target cancels it.
* Step settles a shot's queue state before publishing about it, so a throwing
  presentation observer costs its own notification and cannot re-commit or
  re-resolve the shot on every later tick.

Tests: FireControlPerWeaponTests (new; includes a fused-beam fixture), the
arming tests in FireControlCut124Tests rewritten to the literal distance,
the hull-inclusive probe premises deleted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 18:36:59 +02:00
Meta Vens
96c2f6ba8d Record the literal arming distance and demoted-round rulings
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 18:28:06 +02:00
Meta Vens
d6f1e19251 Record AI per-weapon firing, HUD outcome forecast, and free refusal rulings
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 17:42:49 +02:00