While the asset is losing hull, target what is pressing IT — ranked by distance to the asset minus credit for closing on it — instead of what is nearest to us. Trigger and ranking both read the live hull (pos+0x154), so nothing is inferred. DEFEND engaged 1.9 s after the asset's first hit and held 54% of a 330 s run. It did NOT measurably save the asset: over the window two runs share, the policies are equal to within noise (t=239: 23218 vs 23038). Two reasons, both recorded rather than papered over: the runs are not comparable past that window (spawn timing differs and the hostile count GREW 134->166 in one, fell 147->118 in the other), and the real bottleneck is lethality — the guns are on for 12% of combat frames because the target is outside the 9 deg cone the rest of the time. Also corrects a single-run claim in the previous commit: the asset is NOT reliably safe for the first ~170 s. A second run had first damage at t=70 s. The stage does not replay identically; only 'the loss is slow' survives. Fixes a fatal bug the new mode exposed: DEFEND flies at the asset, which sits inside the friendly formation, and the first escort run went hull 1500 -> DEAD in one tick at 2026 units/s, 0.6 s from a friendly destroyer that avoidance thought it would clear by 365 units — the ship's radius is 2000. Keep-out applied only to hostile turrets. Every entity above BIG_RADIUS now gets its own radius + 800 of physical keep-out with braking inside it, whatever its faction.
6.6 KiB
Escort / mission state from guest RAM — every entity's hull
Status: ✅ CONFIRMED (2026-07-30). Capture: tools/re-capture/mission_state.py,
session tools/re-capture/escort_session.sh, Stage 02 from save slot 01, 240 s of
flight, 240 samples at 1 Hz → captures/mission-state-stage02.jsonl.
Screenshot evidence: captures/escort-stage02-hud.png.
The question
own_state.py found the player's hull by anchoring on a solved definition
field — an undamaged craft carries its definition's HP (+0x054), so the live
counter is the copy of that number that falls. Result: hull = position + 0x154
(autopilot).
Stage 02 is an escort, and it is lost when the ACROPOLIS sinks, not when the
player dies: a 240 s run hit GAME OVER with our own hull at 1500/1500. Scoring
that objective needs someone else's hull. So: is +0x154 a property of the
entity class, or of the player object?
Finding — it is class-wide
At the first sample of the run, before this session's fighting had touched them,
pos+0x154 equals the entity's own definition HP across seven classes and
five distinct HP values:
| Class | radius | definition HP |
pos+0x154 at t=0 |
|---|---|---|---|
UN_e007_ADAN_Turret |
22 | 100 | 100.0 (all 60 instances) |
UN_e010_ADAN_Attacker_S |
100 | 500 | 500.0 (all 19) |
UN_f106_TCAF_Destroyer |
2000 | 10000 | 10000.0 |
UN_e106_ADAN_Destroyer |
2100 | 10000 | 10000.0 |
UN_f105_TCAF_Cruiser |
3800 | 30000 | 30000.0 |
UN_e105_ADAN_Cruiser |
3800 | 30000 | 30000.0 |
UN_f101_TCAF_Acropolis |
1400 | 25000 | 25000.0 |
Measured directly by mission_state.py scan at the start of three separate runs:
146/150, 147/150 and 147/150 entities hold exactly their definition's HP at
pos+0x154. The handful that do not sit slightly below it (9800/10000,
29933.3/30000, 9725/10000, …) — the battle is already in progress when the player
launches, so those ships have already been shot at. Nothing read above its HP,
and nothing read an unrelated number, which is what a coincidental offset would
produce.
The value behaves like a live counter, not a copy of the definition:
- it falls under fire — 780 distinct damage events were logged across the run;
- it goes negative at death and the entity then disappears from the heap
(
UN_f106_TCAF_Destroyer→-30.0of 10000, another →-0.0, a third GONE); - the drops match what the HUD draws — the screenshot shows the ACROPOLIS and the destroyer CHARON each with their own health bar, CHARON's already red.
So hull = position + 0x154 for every entity, and the escort objective is
directly scoreable: read the protected ship's hull, normalise by its definition's
HP, done. No new anchor, no value scan.
The escort asset, measured
UN_f101_TCAF_Acropolis, one instance, HP 25000, collision radius 1400.
Its hull over the 240 s run (pilot chasing the nearest hostile fighter, the
current pilot.py behaviour):
t= 0..150s 25000.0 untouched
t= 180.1s 24779.5
t= 210.1s 24149.5
t= 239.1s 23038.2 -1961.8 total, ≈ -600 HP/min once it starts
⚠️ Onset is NOT a fixed schedule — corrected by a later run. From this run alone
it looked like the asset is safe for the first ~170 s. A second run put the first
damage at t = 70 s, and its hostile population grew (134 → 166 ADAN) where this
one's shrank (147 → 118). So the stage is not replaying identically, and "the asset
is untouched early" is a property of one run, not of Stage 02. What survives the
second run is the weaker, still useful claim: the loss is slow — a few hundred to
~1400 HP/min against 25000, so tens of minutes to sink. The earlier GAME OVER
therefore was not a fast loss; it was an undefended one.
Also captured
- Hostile population fell 147 → 118 over the run (the pilot fired on 435 of 1913 engage frames; most of the remainder it was manoeuvring with the target outside the 9° firing cone).
- Two friendly destroyers were lost while the pilot was elsewhere.
- The HUD's
REMAINING OBread 012 at t≈240 s while 118 ADAN entities were alive, so that counter is objectives, not hostiles — its RAM address is still unknown (❔ open).
Escort-weighted targeting — implemented, and what it did NOT fix
pilot.py gained a DEFEND mode (2026-07-30): while the asset is losing hull,
target the hostiles pressing it — ranked by distance to the asset minus credit for
closing on it — instead of the ones nearest to us. Trigger and ranking both read the
live hull, so nothing is inferred.
It works mechanically: DEFEND engaged 1.9 s after the asset's first hit in one run (t=167.0), and held for 54 % of a 330 s run. But it did not measurably save the asset. Over the window the two policies share, they are the same to within noise:
| t (s) | nearest-fighter | escort-weighted |
|---|---|---|
| 120 | 25000.0 | 24910.0 |
| 180 | 24779.5 | 24460.0 |
| 239 | 23038.2 | 23218.0 |
Two honest reasons it cannot yet be scored better than "no worse":
- The runs are not comparable past that window — different spawn timing and, in the escort-weighted run, a hostile population that grew 134 → 166 while the baseline's fell 147 → 118.
- Lethality is the real bottleneck, not target choice. The guns are on for only 12 % of combat frames (320 of 2630); the rest of the time the target is outside the 9° firing cone while the loop manoeuvres. Choosing a better target does little when most passes do not shoot.
One bug found and fixed by the first escort run (worth keeping as a pattern): the
new mode flies at the asset, which sits inside the friendly formation, and the run
ended hull 1500 -> DEAD in a single tick at 2026 units/s, 0.6 s from a friendly
destroyer the avoidance expected to clear by 365 units — against a hull of radius
2000. Keep-out had been applied only to hostile turrets. Every entity above
BIG_RADIUS now gets a physical keep-out of its own radius + 800, with braking
inside it, whatever its faction; the next run survived its full 330 s untouched.
Reimplementation notes
- Defeat conditions for an escort stage are readable as: protected-asset
hull ≤ 0, or playerhull ≤ 0. - Every unit's effective HP is the definition's
HP, confirmed live for 7 classes — the same field the unit struct already solves statically, so disc data and runtime agree. - Entity removal on death is observable (the object leaves the heap), which gives a clean lifetime signal for anything modelling spawn/despawn.