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.
133 lines
6.6 KiB
Markdown
133 lines
6.6 KiB
Markdown
# 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`](captures/mission-state-stage02.jsonl).
|
|
Screenshot evidence: [`captures/escort-stage02-hud.png`](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](autopilot-memory-driven.md)).
|
|
|
|
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.0` of 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 OB` read **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":
|
|
|
|
1. **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.
|
|
2. **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 player `hull ≤ 0`.
|
|
- Every unit's effective HP is the definition's `HP`, confirmed live for 7 classes —
|
|
the same field the [unit struct](structures/unit-struct-runtime.md) 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.
|