Commit graph

2,392 commits

Author SHA1 Message Date
Meta Vens
ff75b8ea34 Scenarios Cut 1: a stance survives docking; it goes only with an entity gone for good
Every entity's zone-removal subscription dropped its IFF override for whatever
left the zone, and TryDock removes the docking ship, so a scenario stance was
lost one way (the other entity forgot; the docked ship, deactivated, kept its
own) as soon as the player docked. The removal event already tells the two
apart: TryDock parents the ship to its dock before removing it, and every
leaving-for-good path (death, warp, unload) leaves with no parent. The override
is now dropped only for an entity that leaves unparented.

Tests: a hostile stance holds both ways, including the live hostility each side
tracks, across dock and undock; a destroyed entity takes the player's stance
toward it with it. UndockingAdmitsWithNoAgent commits Soul's probe: undock
admits unpiloted, active, with no agent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:09:12 +02:00
Meta Vens
929020e7df Scenarios Cut 1: a quiet arena drops a story station's turrets too
The quiet-arena test ran with an empty Narrative directory, so the story-station
branch of ZoneGenerator never ran under Ambient: false and keeping its turrets
went unnoticed (Soul's S06). The test now places a story station in the entrance,
as the narrative loader would, and checks it is generated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:00:44 +02:00
Meta Vens
178897ce7a Scenarios Cut 1: a failed staging reports what it admitted, not a crash
"Zone unchanged on failure" was defended only by xunit formatting an entity
sequence until the test host overflowed its stack. SafeAssert.Unchanged compares
the zone's entities and agents by identity and order and names what changed by
hull name; every Assert.Contains over zone.Entities in RunStartTests goes through
SafeAssert.In for the same reason.

AMissingDesignFailsNamedAndChangesNothing gains the partial-admission case: a
player and a piloted entity that build cleanly, then a later entity whose preset
is missing. Admitting the built player before the all-or-nothing return now fails
by name ("the zone's entities changed, 5 -> 6: added [Djinni]").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:59:50 +02:00
Meta Vens
7e7c08bc73 Scenarios Cut 1: pin that admission activates
An admitted entity that was never activated tracks no hostility, so
targeting and grudges never see the stance. The stance test now also reads
each side's live EntityHostility, which only activation populates; dropping
Activate from Zone.Admit survived the suite before this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 13:59:43 +02:00
Meta Vens
2724a0d8a7 Scenarios Cut 1: observe an unpiloted ship's steering, not its position
The zone's gravity moves every ship, so an unpiloted scenario ship drifts
(about 14 m over 5 s at the test's spot) with nothing flying it. The rule is
"no agent steers it": the test now checks its movement and look inputs stay
untouched every tick, and that it still has no agent afterwards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 13:10:03 +02:00
Meta Vens
72c58d365f Scenarios Cut 1: a bare hull updates
Entity.Update walks _orderedEquipment, which only TryEquip and TryUnequip
set, so an entity nothing was ever equipped on threw a
NullReferenceException on its first tick. Scenario presets stage bare hulls
(a turret, an unpiloted target), and RunStartTests found it: MapEntity now
sets the ordered equipment to the hull alone when it adds the hull.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:52:44 +02:00
Meta Vens
f527154e26 Scenarios Cut 1: StartGame stages through RunStart; the game's joins admit
StartGame's new-run branch no longer generates, unpacks or admits the
player: RunStart generates the arena and stages the run, and StartGame
enters the zone and binds the returned player (docs/scenarios-cut.md, 2.2,
deletion line). PopulateLevel's warp arrival and the spawnturret console
command admit through Zone.Admit, unpiloted. The console commands stay (Q4);
give's hull route moors a ship at the docked entity and joins a zone only
through Entity.TryUndock, which already admits.

Needs the Unity batchmode compile; Assembly-CSharp is not built headless.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:35:19 +02:00
Meta Vens
85ebe036e1 Scenarios Cut 1: RunStart stages a new run; the Scenario catalog type
RunStart (ServerShared, no Unity type) owns what a new run starts with
(docs/scenarios-cut.md, 2.2):
- GenerateArena: the galaxy's entrance pack, generated under the scenario's
  Ambient rule. ZoneGenerator.GenerateZone gains `ambient`; false keeps
  planets, orbits and stations and generates no ship, turret or wanderer.
- Check: every failure staging a scenario would hit, found with no zone,
  touching no store.
- Stage: all-or-nothing. With no scenario, the generated starting-hull ship
  at the origin, as StartGame built it. With one, the player preset and the
  entities at their authored place and heading with cargo, stance set both
  ways through SetIff, a Minion for each piloted one; all through Zone.Admit.

Scenario (aetheria.scenario v1) joins AetheriaStores.CatalogTypes.
RunStartTests stage into a real prelude arena from the shipped catalog and
authored settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:35:18 +02:00
Meta Vens
5706b657e3 Scenarios Cut 1: Materialize builds product-less designs unbranded; the hull picks the entity
Two rules on Loadouts.Materialize (docs/scenarios-cut.md, 2.2, Q3):
- A design no product makes is outside the economy and is built unbranded,
  with no maker, at Loadouts.UnbrandedQuality (.95, the console give's).
  A design whose products are all unavailable still fails, as before.
- The hull's type picks the entity: a ship hull builds a Ship, a turret hull
  an OrbitalEntity with no orbit; a station hull is refused. Materialize now
  returns Entity.

Loadouts.Resolve is the one rule for how a design is built; RunStart uses it
for scenario cargo. LoadoutTests.MissingDesignReports no longer uses the
product-less "Orphan" as a failure (it now builds); two tests pin the Q3 rule
and the unavailable-product contract it does not loosen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:33:00 +02:00
Meta Vens
fbda4c74fb Scenarios Cut 1: Zone.Admit is the one admission into a zone
Zone.Admit(entity, piloted) adds the entity to Entities, activates it and,
when piloted, gives it the zone's Minion. It is the only writer of
Entities.Add and the only creator of agents (docs/scenarios-cut.md, 2.2).
The constructor's pack loop admits every packed ship but the player's as
piloted and keeps its own origin-scatter rule, which is generation
placement, not admission. Entity.TryUndock admits the undocked ship
unpiloted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:32:59 +02:00
Meta Vens
702b454bea Scenarios map: record Q1-Q4 rulings; Cut 0 cancelled
Q1 A, Q2 B, Q3 A. Q4: every console command stays ("never toss a
single one"), so Cut 0 is cancelled and give/spawnturret admit through
Zone.Admit in Cut 1. Self restores the smoke weapons in the operator
tree when Cut 4 lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:14:40 +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
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
Meta Vens
14f666347c Pin the arming-distance boundary: a Range equal to it fires
The clean mutation run left one survivor, `>=` for `>` in Fire's refusal.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 17:27:47 +02:00
Meta Vens
543edee358 Guided rounds follow the committed burst and resolution; no-target launcher no longer throws
- GuidedProjectileManager passed target.transform to OnFireGuided for a
  no-target fused round: a NullReferenceException inside InstantWeapon.Execute
  after the shot was queued, which skipped audio, wear, heat and visibility
  and unwound Zone.Update. The tuple's transform is null-safe now, and the
  trackmissile console command falls back to the missile.
- Presentation follows the commit rather than predicting it. The manager binds
  each guided round to Zone.ShotCommitted (retarget to the burst point the
  commit names, so a Contact round bursts at its hull and a Range-clamped
  burst is not overshot) and ShotResolved (burst there, then). The binding is
  disposed when the projectile dies, so a pooled round does not accumulate them.
- Beams and barrel-fired projectiles stay with F5.

Unity-only; compile-checked by reading.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 17:05:15 +02:00
Meta Vens
24b4071b69 Arming distance: no round bursts nearer than its blast radius past the shooter's hull
Operator ruling 2026-09-30. A fused round's burst is at least BlastRadius
beyond the far edge of the shooter's own hull, measured along the flight line
against the hull as it stood at fire (HullAhead's exit). A close burst -- a
target inside that distance, in or out of arc -- is pushed out along the aim
and carries no target; a contact round is floored to it too.

- The F3 trigger gate is deleted. Fire refuses a round (returns 0) whose
  arming distance exceeds Range, per round, where Range is read after Execute
  refreshed it; InstantWeapon and ConstantWeapon skip a refused round.
- PFire's range, Inspect's range and the range clamp are planar (R7).
- HullEntryDistance becomes HullAhead (entry and exit); the max(...,0) clamp
  on entry is gone, the arming floor covers it.
- ShotOutcome.HasBurstPoint names the rounds that have a frozen burst point.
- SafeAssert: the tests no longer hand xunit an Entity, PendingShot or
  ShotOutcome to format; a failing assertion had been overflowing the test
  host's stack.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 17:05:14 +02:00
Meta Vens
2d4474f859 Record out-of-arc, arming-distance and pivot-arc rulings
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 16:49:36 +02:00
Meta Vens
9c0029931a Presentation follows the committed shot, not the selection (F7)
EntityInstance handed effect managers the shooter's selected target whatever
FireControl decided. A fused round whose target was invalid or out of arc
flies at none, and GuidedProjectileManager dropped a launcher shot with no
target entirely, so a real burst had no missile.

- Zone.TryGetShot: a presentation reads the shot the simulation committed
  (its target, its burst point) by the ShotId OnFire hands it.
- EntityInstance passes the shot's own target; a launcher round with none
  flies to the shot's burst point.

Unity-only; compile-checked by reading.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 16:27:54 +02:00
Meta Vens
640fa96787 Fused rounds fire out of arc; safe fuse; proximity misses type as Burst
Operator ruling 2026-09-30: a fused weapon with a selected but out-of-arc
target fires. Its round flies along the arc-clamped aim and bursts at the
target's range. Non-fused weapons still refuse.

- FireControl.ArcPermitsFire is the one trigger arc gate (fused weapons are
  exempt); Fire reads the same InArc through PFire's new designated/inArc
  outs, which replace `bears`. No valid targeting data still bursts at Range.
- F3: a fused weapon whose effective Range does not exceed its BlastRadius
  stays safed at the trigger (FuseCanArm, read from the runtime Range). The
  catalog check on Range.Min is deleted; it read the authored floor, which
  Evaluate can scale below.
- F4: a targeted proximity burst is pulled back along its line to Range.
- F8: a proximity round that detonates commits as Burst (or Hit); Apply's
  Proximity case collapses into the Burst path via Outcome.BurstPoint.
- Tests: arc-only and LockWeapon gates, out-of-arc burst, trigger gate,
  runtime-Range refusal, Range clamp, Burst typing, commit horizon, and
  Soul's adopted probes (aim clamp edges, contact point, target leaves,
  shooter destroyed).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 16:27:46 +02:00
Meta Vens
6133a051a6 Merge origin/codex/fire-control-12 into hands/fuse-fix2 2026-09-29 16:11:47 +02:00
Meta Vens
e7bb1e14f0 Record the airburst intent ruling
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 14:02:42 +02:00
Meta Vens
91ed4ef91b Assert on the shot target and outcome identity without formatting entities
Assert.Null(shot.Target) and Assert.Same(committed, resolved) format the
entity graph when they fail, which overflows the xunit stack and crashes the
host instead of reporting a red. Use Assert.True with a message.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 13:50:41 +02:00
Meta Vens
f5c6729e7a No-lock fused rounds: aim, invalid targets, contact stops, outlived shooters
Operator rulings 2026-09-30 after Soul's pass on 8c002637:

- A no-lock round flies along Entity.LookDirection restricted by the mount's
  arc (FireControl.AimDirection: inside InArc's cone as is, outside it turned
  to the cone's edge; no aim falls back to the mount). The simulation had no
  aim clamp before; the old code used the hull-fixed mount direction.
- An invalid target (out of range, out of arc, not visible, not locked) counts
  as no lock for a fused round. PFire's gate is the one decider and now also
  reports `bears`; Fire reads it. The round carries no target afterwards.
- A no-lock Contact round bursts at the first hull its aim line crosses
  before max range, read through Lane's own cell admission (CellsOnLine,
  split out of Lane). Proximity and Delayed keep the max-range burst. Such a
  round commits at Fire, since it has no target facing to read late, and
  arrives when it has flown to the burst point. The seam for mining chunks is
  FireControl.ContactDistance.
- A round outlives its shooter: Step no longer cancels a shot whose source
  left the zone. Resolution reads the zone and the target; the shooter is an
  identity a shot carries. PendingShot.FireOrigin freezes where it left from.
- ShotOutcome.Result (Miss, Hit, Burst) replaces the stored Hit bool, which
  is now derived. A burst without a target is no longer reported as a miss,
  and the debug HUD labels it BURST.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-29 13:27:43 +02:00