diff --git a/docs/re/INDEX.md b/docs/re/INDEX.md index a8a2edb..8f39380 100644 --- a/docs/re/INDEX.md +++ b/docs/re/INDEX.md @@ -33,6 +33,7 @@ Promote to a prose `structures/…md` file when a format needs behavioural notes | IDXD object layout solver | ✅ | [`tools/re-capture/weapon_runtime.py`](../../tools/re-capture/weapon_runtime.py) | Scan RAM for a class's vtable → enumerate its objects → brute-force `(field, offset, encoding)` against the disc records. Accepts a binding only on **zero** contradictions. Generalizes to any IDXD-backed definition | | Live entity state, anchored on the definition | ✅ | [`tools/re-capture/own_state.py`](../../tools/re-capture/own_state.py) · [autopilot](autopilot-memory-driven.md) | An undamaged craft holds its definition's own numbers, so a *solved definition field* locates the matching live field without a value scan: definition `HP` (1500) → **hull at `position+0x154`**, confirmed by a trace across a death (30/60/90 per hit, negative at 0). Reusable for any live counter whose maximum the definition carries | | Mission / escort state, every entity's hull | ✅ | [`tools/re-capture/mission_state.py`](../../tools/re-capture/mission_state.py) · [escort state](mission-escort-state.md) | `hull = position + 0x154` is a property of the **entity class**, not of the player object: at t=0 it equals each entity's own definition `HP` across 7 classes and 5 distinct HP values (turret 100, fighter 500, destroyer 10000, cruiser 30000, **ACROPOLIS 25000**), falls under fire (780 damage events in 240 s), goes negative at death, and the object then leaves the heap. So an escort objective is scoreable live — `UN_f101_TCAF_Acropolis` measured at 25000 → 23038 over 240 s, attack starting only at t≈170 s. `REMAINING OB` counts objectives, not hostiles (012 on the HUD vs 118 live ADAN); its address is still ❔ | +| In-flight control mapping | ✅/🟡 | [`tools/re-capture/fire_probe.sh`](../../tools/re-capture/fire_probe.sh) · [controls](flight-controls-runtime.md) | Measured by holding each pad input and photographing the HUD ammo counters: **`RB` = nose gun** (6000→5956 in 4 s, ~11 rounds/s, HEAT rises), **`Y` = main mount** (missiles, 300→299), d-pad = **tactical map** overlay, nothing else moves a counter. No target-cycle input exists — the `TARGET` marker is present with nothing pressed, so targeting is automatic and a missile lock is **time-on-target**. That, not target choice or ballistics, is what caps lethality at 2 kills per 98 missiles | | Input → dynamics calibration | ✅ | [`tools/re-capture/ctrl_probe.py`](../../tools/re-capture/ctrl_probe.py) · [`binq.py`](../../tools/re-capture/binq.py) | Hold each pad input in turn and measure the craft's speed as displacement/s of its own position triple — no speed field needed first. Settled the throttle: **`RT` accelerates, `LT` brakes, and the setting persists** (488 → 1510 → 174 units/s), overturning an earlier field-scan conclusion | ## Functions / code paths diff --git a/docs/re/captures/dpad-tactical-map.png b/docs/re/captures/dpad-tactical-map.png new file mode 100644 index 0000000..2a9b33d Binary files /dev/null and b/docs/re/captures/dpad-tactical-map.png differ diff --git a/docs/re/flight-controls-runtime.md b/docs/re/flight-controls-runtime.md new file mode 100644 index 0000000..ec0a471 --- /dev/null +++ b/docs/re/flight-controls-runtime.md @@ -0,0 +1,46 @@ +# In-flight control mapping — measured, not assumed + +**Status:** ✅ for the weapon bindings (ammo counters move), 🟡 for the rest (HUD +observation only). Probes: `tools/re-capture/fire_probe.sh` (hold each input, photograph +the ammo counters) and `lock_probe.sh` (tap each, watch the reticle). Stage 02, in flight. +Evidence: [`captures/fire-probe-ammo-counters.png`](captures/fire-probe-ammo-counters.png). + +| input | effect | confidence | +|---|---|---| +| **`RB`** | **Nose gun.** `NOSE BM` 06000 → 05956 in a 4 s hold ≈ **11 rounds/s**; `HEAT` bar rises | ✅ | +| **`Y`** | **Main mount** (missiles). `MAIN MPM` 00300 → 00299 per tap | ✅ | +| **`RT` / `LT`** | Throttle up / brake, a *persistent* setting (488 → 1510 → 174 u/s) | ✅ (earlier session) | +| **d-pad** | **Tactical map** overlay (grid with contact blips) — not target cycling | 🟡 | +| `LB`, `X`, `B`, `A`, `LS`, `RS` | No change to either ammo counter | ✅ (as "not a weapon") | + +## Targeting appears to be automatic, and that is the lethality problem + +No input was found that cycles a target. The green `TARGET` marker is already present in +idle frames with nothing pressed, so the game selects for us — most likely whatever sits +nearest the reticle — and a guided missile's lock is then a **time-on-target** mechanic +rather than a button. + +That fits the measurements end to end: + +- the guns fire fine (11 rounds/s) but the kill counters read `0000` after five gun-only + runs → **we shoot and miss**; +- guided missiles (`Missile_P`, Power 200, `GuidanceType` 5) got the first kills, + `WARPLANES 0002`, but only **2 per 98 launches**; +- the pilot's own log shows aim error wandering between ~10° and ~40° for most of a + pass. + +So the remaining bottleneck is **not** the target choice (escort weighting), **not** the +ballistics (now taken from the confirmed `Shell` records), and **not** the input mapping +(measured above). It is that the steering loop never holds the nose on one contact long +enough to complete a lock. The next thing worth doing is making aim *dwell* — and the +cheapest oracle for whether a lock ever completes is the reticle itself, or finding the +lock timer in the player object with the same anchoring trick used for hull. + +## Notes for the reimplementation + +- Two independent weapons with separate ammo pools and separate HUD counters: + `NOSE BM` (gun, 6000) and `MAIN MPM` (missiles, 300). +- The gun has a **HEAT** bar that fills while firing — a sustained-fire limit the + reimplementation needs; its cap and cool-down rate are not measured yet. +- The tactical map is a full-screen overlay bound to the d-pad and does not pause flight + (the craft kept taking fire with it open). diff --git a/tools/re-capture/lock_probe.sh b/tools/re-capture/lock_probe.sh new file mode 100755 index 0000000..2075425 --- /dev/null +++ b/tools/re-capture/lock_probe.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Which input SELECTS a target, and does a lock then build? +# +# 98 guided missiles produced 2 kills. A guided missile with nothing to guide to +# flies straight, so the suspicion is that the loop has never selected a target +# at all: the HUD carries a `TARGET` marker and a lock reticle, and no button in +# pilot.py has ever touched them. fire_probe.sh already showed LB/X/B/A/LS/RS do +# not discharge a weapon — but "does not fire" says nothing about "does not +# select", so sweep them again watching the RETICLE instead of the ammo. +# +# Captures the centre of the screen (reticle + lock brackets) and the right-hand +# target panel, so a selection or a building lock is visible either way. +set -u +export HOME=/sylph-home/re SDL_AUDIODRIVER=dummy DISPLAY=:98 +SD="$(cd "$(dirname "$0")" && pwd)" +SHOTS=/sylph-home/re/shots +HOLD="${1:-3}" + +shot(){ screenshot "$SHOTS/lock-$1.png" >/dev/null 2>&1; echo " shot $1"; } + +"$SD/launch_mission.sh" fly || { echo "BOOT FAILED"; exit 1; } +sleep 3 +shot "00-idle" + +# tap, not hold: a select is an edge, and holding a cycle button would just spin +# through every contact. Two taps each, so a cycle that lands on nothing the +# first time still shows on the second. +for b in RS LS LB B X A Y; do + vgamepad tap "$b" 200; sleep 0.4; vgamepad tap "$b" 200; sleep "$HOLD" + shot "tap-$b" +done +vgamepad reset +shot "99-final" +echo "PROBE DONE"