From 87ef6a85f9cbb72a19fe85035f72173bee3edffb Mon Sep 17 00:00:00 2001 From: arcattack Date: Thu, 23 Jul 2026 08:15:55 -0500 Subject: [PATCH] Console: STAGING visibility + launch re-arm in launch-with-whoever mode Field report (2026-07-23): with open seats the roster stays blue until LAUNCH -- correct (loading cannot start before the roster is finalized, and the LAUNCH press is what finalizes a partial roster) -- but the operator lost all visibility into the post-launch staging, and worse, the LAUNCH button's re-arm rode the "WAITING FOR OPERATOR" line that only prints on a FULL-roster ACK, so in partial mode the button stayed dead after round 1. - The relay's 10s "launch HELD -- still loading: PLAYER n (k/m ready)" lines now surface as the status banner ("STAGING -- PLAYER 2 still loading (1/2 ready)") while the per-seat lights walk teal->green. - "round RESET" re-arms the console (launched/ready/held cleared) so every round's LAUNCH works regardless of roster fullness. - The standby banner explains the mode: seated players are STANDING BY, LAUNCH starts their loading, the mission fires itself at all-green. - The seated count includes registered/ready players (it undercounted once pods advanced past blue). Monitor states unit-tested (held -> launch -> clear -> reset -> re-arm). Co-Authored-By: Claude Opus 4.8 (1M context) --- tools/btoperator.py | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/tools/btoperator.py b/tools/btoperator.py index d58e9f8..decdf8c 100644 --- a/tools/btoperator.py +++ b/tools/btoperator.py @@ -70,6 +70,9 @@ class SessionMonitor: r"PLAYER (\d+) LEFT \(host \d+\) tag='([^']*)'") RE_RELAY_READY = re.compile( r"PLAYER (\d+) READY \(host \d+\) tag='([^']*)'") + RE_RELAY_HELD = re.compile( + r"launch HELD -- still loading: (.+) \((\d+)/(\d+) ready\)") + RE_RELAY_RESET = re.compile(r"round RESET") RE_MESH_EGG = re.compile(r"\[([^\]]+)\] egg sent") RE_MESH_RUN = re.compile(r"\[([^\]]+)\] RunMission #(\d+) sent") @@ -82,6 +85,7 @@ class SessionMonitor: self.launched = False self.stats = "" self.seat_info = {} # tag -> (callsign, mech) requests + self.held_status = None # "PLAYER 2, 3 (2/4 ready)" while staging def host_tag(self, host_id): i = host_id - 2 # hostIDs 2..N+1 in egg order @@ -135,9 +139,24 @@ class SessionMonitor: if tag and self.state.get(tag) != ST_IDLE: self.state[tag] = ST_IDLE changed = True + m = self.RE_RELAY_HELD.search(line) + if m: + self.held_status = "%s still loading (%s/%s ready)" % ( + m.group(1), m.group(2), m.group(3)) + changed = True + if self.RE_RELAY_RESET.search(line): + # BETWEEN ROUNDS: re-arm the console for the next launch. The + # old re-arm rode "WAITING FOR OPERATOR" which only prints on a + # FULL-roster ACK -- in launch-with-whoever mode the button + # stayed dead after round 1 (field report 2026-07-23). + self.launched = False + self.ready = False + self.held_status = None + changed = True m = self.RE_RELAY_RUN.search(line) if m and int(m.group(1)) >= 2: self.launched = True + self.held_status = None for t in self.state: if self.state[t] == ST_REGISTERED: self.state[t] = ST_LAUNCHED @@ -755,21 +774,25 @@ class Operator(QMainWindow): for tag, state in self.monitor.state.items(): parts.append(' %s %s' % (ST_COLORS[state], tag, state)) - seated = sum(1 for s in self.monitor.state.values() if s == ST_SEATED) + seated = sum(1 for s in self.monitor.state.values() + if s in (ST_SEATED, ST_REGISTERED, ST_READY)) ready_n = sum(1 for s in self.monitor.state.values() if s == ST_READY) if self.monitor.launched: head = "LAUNCHED — mission running" if (self.console_proc and not self.end_btn.isEnabled() and not getattr(self, "end_sent", False)): self.end_btn.setEnabled(True) + elif self.monitor.held_status: + head = "STAGING — " + self.monitor.held_status elif ready_n: head = ("%d pod(s) LOADED+READY — LAUNCH starts them instantly" % ready_n) elif self.monitor.ready: head = "ALL PODS READY — press LAUNCH MISSION" elif seated: - head = ("%d player(s) seated — LAUNCH MISSION starts with " - "whoever is here" % seated) + head = ("%d player(s) seated (standing by) — LAUNCH starts " + "their loading; the mission fires itself when every " + "light is green" % seated) elif self.monitor.relay_mode: head = "eggs delivered: %d/%d" % (self.monitor.eggs, len(self.monitor.tags))