tools: wait_flight was testing a pixel that is 40 rows outside the HUD

It tested one named pixel, (450,640), described as "inside the SHIELD bar". It is
not inside anything. `screenshot` crops to the GAME SURFACE, 1279x675, while
those constants were chosen for a 1280x720 window: measured on a frame that was
unmistakably in flight (TIME 01:18.05, REMAINING OB 004), the SHIELD bar's green
rows are 590-605 and the ARMOR bar's are 650-670, and (450,640) sits in the gap
between them reading (64,66,116) - blue.

That is why the corpus already carried "wait_flight.sh reported NEVER REACHED
FLIGHT while the game was plainly in flight" as an unexplained note, and it is
exactly the failure screen_id.py's own header warns about. Reproduced live here:
the script sat for five minutes reporting nothing while tapping A into the
cockpit every six seconds.

Now classified by whole-image statistics - screen_id.py's "flight" class, green
fraction 1.3-1.5% against <0.1% on every menu - which no crop or scale can move.
Two consecutive frames are required so an explosion in a cutscene cannot pass,
and the A tapping (which dismisses the objective card) STOPS once flight is seen,
because in the cockpit A is a weapon press and not a "continue".

Tested against the running game before being believed: 0 with "IN FLIGHT at 19s"
on the same session the old test had been failing on, and then in the unattended
launch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
This commit is contained in:
Sylpheed RE agent
2026-08-23 18:02:27 +00:00
parent 8bea94a47f
commit 2888af5d7b

View File

@@ -5,23 +5,34 @@
# stage load and the objective card. Under lavapipe that is not a constant: one
# run needed 75 s, the next was still in Raymond's dialogue at 140 s, so the A
# meant for the OBJECTIVE card was swallowed by the cutscene and the card sat
# there forever.
# there forever. So poll, and tap A every few seconds until the HUD appears
# (which dismisses the objective card whenever it happens to be up).
#
# The in-flight HUD is unmistakable: the SHIELD bar is a solid bright green
# block at the bottom of the screen, and no cutscene or menu has anything green
# there. So poll that pixel, and tap A every few seconds until it appears (which
# dismisses the objective card whenever it happens to be up).
# 🔴 REWRITTEN 2026-08-23. This script used to test ONE NAMED PIXEL, (450,640),
# "inside the SHIELD bar". It is not inside anything: `screenshot` crops to the
# GAME SURFACE, which is **1279x675**, while those constants were picked for a
# 1280x720 window — (450,640) lands in the gap between the SHIELD bar (green rows
# 590-605) and the ARMOR bar (650-670) and reads (64,66,116), blue. Measured on a
# frame that was unmistakably in flight, `TIME 01:18.05`, while this script sat
# there reporting nothing and tapping A into the cockpit every six seconds. That
# is exactly the failure `screen_id.py`'s own header warns about, and it is why
# the corpus already carried "wait_flight.sh reported NEVER REACHED FLIGHT while
# the game was plainly in flight" as an unexplained note.
#
# So classify by WHOLE-IMAGE statistics instead, which no crop or scale can move:
# the flight HUD paints green over the entire frame (green fraction 1.3-1.5 %,
# against <0.1 % on every menu), and that is `screen_id.py`'s `flight` class.
#
# Same liveness rule as skip_intro.sh, for the same reason: a failed screenshot
# leaves r/g/b unset, they default to 0, the green test fails, and waiting for
# the HUD becomes indistinguishable from waiting for a dead emulator.
# leaves the classification unset and waiting for the HUD becomes
# indistinguishable from waiting for a dead emulator.
set -u
export HOME=/sylph-home/re
DISP="${DISPLAY:-:98}"
SD="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
alive(){ ps -o pid=,stat= -C xenia_canary 2>/dev/null | awk '$2 !~ /^Z/ {print $1}'; }
DEADLINE=$(( SECONDS + ${1:-240} ))
PX=450; PY=640 # inside the SHIELD bar of the flight HUD
last_tap=0
hits=0; last_tap=0
while [ $SECONDS -lt $DEADLINE ]; do
rm -f /tmp/wf.png
if ! screenshot /tmp/wf.png >/dev/null 2>&1 || [ ! -s /tmp/wf.png ]; then
@@ -30,16 +41,20 @@ while [ $SECONDS -lt $DEADLINE ]; do
sleep 2; continue
fi
[ -n "$(alive)" ] || { echo "EMULATOR GONE at ${SECONDS}s"; exit 4; }
read -r r g b < <(convert /tmp/wf.png -format \
"%[fx:int(255*p{$PX,$PY}.r)] %[fx:int(255*p{$PX,$PY}.g)] %[fx:int(255*p{$PX,$PY}.b)]" info:)
if [ "${g:-0}" -gt 140 ] && [ $(( g - r )) -gt 60 ] && [ $(( g - b )) -gt 60 ]; then
echo "IN FLIGHT at ${SECONDS}s (HUD shield bar visible)"
exit 0
fi
if [ $(( SECONDS - last_tap )) -ge 6 ]; then
python3 "$(dirname "${BASH_SOURCE[0]}")/pad.py" tap A 0.3
last_tap=$SECONDS
cls="$(python3 "$SD/screen_id.py" /tmp/wf.png | awk '{print $1}')"
if [ "$cls" = "flight" ]; then
hits=$(( hits + 1 ))
# Two consecutive frames: one green-heavy frame could be an explosion in a
# cutscene. Do not tap A once flight has been seen — in the cockpit that is
# a weapon/target press, not a "continue".
[ $hits -ge 2 ] && { echo "IN FLIGHT at ${SECONDS}s (screen_id: flight)"; exit 0; }
else
hits=0
if [ $(( SECONDS - last_tap )) -ge 6 ]; then
python3 "$SD/pad.py" tap A 0.3
last_tap=$SECONDS
fi
fi
sleep 2
done
echo "NO FLIGHT HUD within ${1:-240}s"; exit 1
echo "NO FLIGHT HUD within ${1:-240}s (last: ${cls:-none})"; exit 1