pilot: target select is A pressed TWICE — from the HUD tutorial, not from probing

HEADS-UP DISPLAY tutorial, verbatim: 'Press A twice to target the enemy closest
to the center of the screen.' A double tap, which is exactly why every button
sweep in flight-controls-runtime.md found nothing and why I concluded targeting
was automatic — each sweep tapped once. It also explains the missiles:
GuidanceType 5 guides to the GAME's selection and the loop had never made one,
so 98 launches guided to nothing.

Wired in: double-tap A when the committed contact is already within 14 deg of
the nose, so the game's choice and ours are the same object. One run: 8 kills
from 66 missiles (12% per missile) against the previous 9 from 101 (8.9%). The
absolute count is inside run variance and the efficiency gain is one sample, so
neither is claimed as decisive — it needs repeat runs.

Also documents that expository tutorials self-advance while interactive ones
stall (BASIC CONTROLS waits forever on 'Go to the box'), and that captions need
cropping across many frames because they type out.
This commit is contained in:
claude-re
2026-07-30 20:47:54 +00:00
parent 58f421d896
commit 4821ba7fea
3 changed files with 46 additions and 4 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 KiB

View File

@@ -72,9 +72,26 @@ the craft's response to a given stick deflection is configurable. Any calibratio
against one profile (e.g. the `ctrl_probe.py` throttle numbers) is only valid for the
save's current settings.
**Next:** bind physical buttons to actions 914 by watching the *reticle and target
panel* (not the ammo counters) — and once `Change Target` and `Padlock` are identified,
re-run the missile test with a locked target.
## What the tutorials state outright
`tutorial_capture.sh <index> <secs> <tag>` plays one lesson and photographs it. Captions
use a typewriter effect, so crop `900x125+160+40` from many frames to read a full
sentence. Lessons that require the player to *do* something stall (BASIC CONTROLS sits
on "Go to the box on your screen" forever with nobody flying); the expository ones run
on their own.
- **HEADS-UP DISPLAY (index 1):** *"Enemies are displayed with **red markers** and allies
with **blue markers**." · "Targeting an enemy displays an Armor Gauge…" ·* **"Press Ⓐ
twice to target the enemy closest to the center of the screen."**
- **ADVANCED CONTROLS (index 5):** `B`+`LS` = Side Roll / 180 Degree Turn / Level Off ·
`B`+`A` together = face the target · `LT`+`RT` together = *"sets your fighter's speed
to that of the target… works well when you are trying to get behind an enemy. Once
behind an enemy, this also helps you attack them."*
**`Change Target` is Ⓐ pressed TWICE** — a double tap. That is why every button sweep in
this document found nothing and why I wrongly concluded targeting was automatic: each
sweep tapped once. It also explains the missiles — `GuidanceType 5` needs the *game's*
selection, and the loop had never made one, so 98 launches guided to nothing.
## Notes for the reimplementation

View File

@@ -159,6 +159,15 @@ class Pilot:
# "Press B and A together to face [the target]" — a snap turn, far quicker
# than winding the PD controller around for a contact behind us.
FACE_MIN = math.radians(999) # 50 deg to re-enable the B+A snap turn
# HEADS-UP DISPLAY tutorial, verbatim: "Press A twice to target the enemy
# closest to the center of the screen." A DOUBLE tap — which is why every
# single-tap button sweep found nothing and concluded targeting was
# automatic. It also explains the missiles: GuidanceType 5 needs the GAME's
# selection, and we had never made one, so 98 launches guided to nothing.
# Select only when our committed contact is already near screen centre, so
# the game's choice and ours are the same object.
SELECT_CONE = math.radians(14)
SELECT_PERIOD = 3.0
FACE_PERIOD = 4.0
def __init__(self, W, pad, dry=False, log=sys.stdout):
@@ -193,6 +202,8 @@ class Pilot:
self.face_t = -1e9
self.face_down = None
self.faces = 0
self.select_t = -1e9
self.selects = 0
def f32(self, off):
b = os.pread(self.W.fd, 4, off)
@@ -254,6 +265,15 @@ class Pilot:
self.pad.trig("LT", 1.0 if want < 0 else 0.0)
self.throttle = want
def select_target(self, t):
"""A, twice: make the GAME target what we are already pointing at."""
if self.dry or t - self.select_t < self.SELECT_PERIOD:
return
self.pad.f.write("tap A 90\n")
self.pad.f.write("tap A 90\n")
self.select_t = t
self.selects += 1
def face_target(self, t):
"""B + A: snap the nose onto the selected target."""
if self.dry or t - self.face_t < self.FACE_PERIOD:
@@ -556,6 +576,11 @@ class Pilot:
if fire != self.firing:
(self.pad.press if fire else self.pad.release)("RB")
self.firing = fire
if (tgt and self.mode in ("ENGAGE", "DEFEND")
and abs(aim[0]) < self.SELECT_CONE
and abs(aim[1]) < self.SELECT_CONE
and tgt[4] < MISSILE_RANGE and pn < 1.2):
self.select_target(t)
if self.face_down is not None and t - self.face_down > 0.2:
self.pad.release("B")
self.pad.release("A")
@@ -577,7 +602,7 @@ class Pilot:
f"thr={str(self.throttle):>5} yaw={math.degrees(yaw):+6.1f} "
f"pit={math.degrees(pitch):+6.1f} aim={math.degrees(aim[0]):+6.1f}"
f"/{math.degrees(aim[1]):+6.1f} fire={int(fire)} msl={self.missiles}"
f" fc={self.faces}")
f" fc={self.faces} sel={self.selects}")
if ast is not None:
msg += f" ast={a_frac*100:5.1f}%"
if a_dmg > self.ASSET_ALERT: