Follow-up to the GameMenu guard, caught by its own instrumentation: 75 menu
escapes across 15 games, ~5 per game, all clustered around game end.
save_and_exit deliberately opens the in-game ESC menu, but callers only pause
the panel check AFTER it returns:
18:58:48.142 game | end | ok
18:58:48.334 In-game menu open - closing it (1/6) <- the guard
18:58:48.487 Clicking SAVE_AND_EXIT_NO_HIGHLIGHT <- the bot
18:58:48.736 In-game menu open - closing it (2/6) <- the guard again
18:58:49.098 Health Manager is now paused <- too late
Games still completed, so this was noise rather than breakage — but it is a
race, and the guard was pressing esc while the shutdown clicked the menu.
save_and_exit now pauses the panel check for the whole sequence and restores it
in a finally, so it cannot leak the paused state if save/exit raises.
The guard itself is working: 15 games, 0 failures, 0 portal failures, and the
loot-filter/Chronicle/Options incidents have not recurred.
MANA> instrumentation has also settled #23 — see the issue.
Co-Authored-By: Claude Opus 5 <[email protected]>
67 lines
2.7 KiB
Python
67 lines
2.7 KiB
Python
"""The in-game ESC menu must be detected and closed.
|
|
|
|
2026-08-28: the bot was found sitting in OPTIONS -> VIDEO with a "settings have
|
|
changed, apply or discard?" modal. Discarding revealed the cause — the in-game
|
|
ESC menu carries these buttons at SCREEN CENTRE:
|
|
|
|
OPTIONS / SAVE AND EXIT / RETURN TO GAME / LOOT FILTER / CHRONICLE
|
|
|
|
A stray esc opens the menu, and the bot's next movement click lands on one of
|
|
them. The HUD mask deliberately leaves screen centre clickable, so nothing
|
|
stops it.
|
|
|
|
That single mechanism explains three separate incidents: the loot filter being
|
|
toggled, CHRONICLE blanking every template match for a whole run, and the video
|
|
options being opened and changed.
|
|
|
|
It has NO close button, so CenterPanel (which matches CLOSE_PANEL_2) cannot see
|
|
it. Bug 31 warned about exactly this: "just send esc is WORSE: with nothing
|
|
open, esc opens the GAME MENU".
|
|
"""
|
|
import inspect
|
|
|
|
|
|
def test_game_menu_screenobject_exists():
|
|
from ui_manager import ScreenObjects
|
|
assert hasattr(ScreenObjects, "GameMenu")
|
|
|
|
|
|
def test_guard_closes_the_menu_before_chickening():
|
|
from health_manager import HealthManager
|
|
src = inspect.getsource(HealthManager)
|
|
assert "ScreenObjects.GameMenu" in src, "the guard never checks for the in-game menu"
|
|
menu_at = src.index("ScreenObjects.GameMenu")
|
|
chicken_at = src.index("Chickening to be safe")
|
|
assert menu_at < chicken_at, "menu escape must come before the chicken path"
|
|
|
|
|
|
def test_mana_threshold_crossings_are_logged():
|
|
"""Issue #23 cannot be settled without this.
|
|
|
|
Mana was only logged when a potion was DRUNK, so a second dip that failed to
|
|
trigger was indistinguishable from mana never dipping twice.
|
|
"""
|
|
from health_manager import HealthManager
|
|
src = inspect.getsource(HealthManager)
|
|
assert "MANA>" in src, "no threshold-crossing log; #23 stays undecidable"
|
|
assert "_mana_below_threshold" in src, "crossings are not edge-triggered"
|
|
|
|
|
|
def test_save_and_exit_pauses_the_panel_check():
|
|
"""The guard must not fight the shutdown it was built to protect.
|
|
|
|
save_and_exit deliberately opens the in-game ESC menu. The health manager
|
|
now closes that menu on sight, and callers only pause AFTER save_and_exit
|
|
returns — so the guard raced the shutdown: 75 escapes across 15 games,
|
|
interleaved with the save/exit clicks.
|
|
"""
|
|
import inspect
|
|
from ui import view
|
|
|
|
src = inspect.getsource(view.save_and_exit)
|
|
assert "set_panel_check_paused(True)" in src, (
|
|
"save_and_exit opens the ESC menu without pausing the panel check, so "
|
|
"the GameMenu guard will close it mid-shutdown"
|
|
)
|
|
assert "finally" in src, "the pause must be released even if save/exit raises"
|