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))