fix(health): detect and close CENTRED panels — Chronicle blocked every template match
Bug 31 fixed the keypress that opened CHRONICLE but explicitly left the real
gap open: the panel guard matches only LeftPanel / RightPanel, and a centred
panel matches neither. Nothing closed it, and a centred panel blanks EVERY
later template search.
Recurred 2026-08-28 with the merc key never pressed at all
(resurrect_merc | skip | merc alive), so something else opened it and it simply
stayed. The error screenshot shows Chronicle filling the screen while the bot
hunted A5_RED_PORTAL for 66s:
Traverse from a5_larzuk to a5_nihlathak_portal
Wanted to select A5_RED_PORTAL, but could not find it
random guess towards (96, -115)
Wanted to select A5_RED_PORTAL, but could not find it
Why the existing guard missed it, measured on that frame: the Chronicle's close
button scores 1.000 against CLOSE_PANEL_2 at (952, 56). That x IS inside
right_panel_header (830,0,455,56) — but the ROI is 56px tall and the button's
centre sits exactly at y=56, so the match falls outside. Both header ROIs
scored 0.409 and 0.373.
Adds a center_panel_header ROI (400,0,620,92) and a CenterPanel ScreenObject,
and escapes it WITHOUT counting toward a chicken — a centred panel is
self-inflicted UI state like the waypoint panel, and chickening on it throws
away a healthy game. Bounded by the same _MAX_WP_PANEL_ESCAPES so one that
genuinely will not close still falls through.
Verified on the failure frame: CenterPanel True, LeftPanel False, RightPanel
False.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
BIN
assets/templates/skills_spellbook/blessed_hammer.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/templates/skills_spellbook/cleansing.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/templates/skills_spellbook/concentration.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
assets/templates/skills_spellbook/conviction.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/templates/skills_spellbook/defiance.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/templates/skills_spellbook/fist_of_the_heavens.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
assets/templates/skills_spellbook/holy_shield.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
assets/templates/skills_spellbook/might.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/templates/skills_spellbook/oak_sage.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
assets/templates/skills_spellbook/prayer.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
assets/templates/skills_spellbook/raven.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/templates/skills_spellbook/redemption.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/templates/skills_spellbook/teleport.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/templates/skills_spellbook/tome_of_identify.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
assets/templates/skills_spellbook/tome_of_town_portal.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
assets/templates/skills_spellbook/vengeance.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
assets/templates/skills_spellbook/vigor.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
@@ -130,6 +130,13 @@ corpse=459,195,414,213
|
||||
chat_icon=7,555,43,41
|
||||
left_panel_header=0,0,455,56
|
||||
right_panel_header=830,0,455,56
|
||||
; A CENTRED panel (Chronicle, and anything else that opens mid-screen) puts its
|
||||
; close button outside both header ROIs above — the Chronicle's X sits at
|
||||
; (952, 56), inside right_panel_header's x-range but exactly at its 56px height
|
||||
; boundary, so the match centre falls out. Nothing then closes it, and a
|
||||
; centred panel blanks every later template search: measured as a 66s
|
||||
; click_red_portal failure on 2026-08-28 (Bug 31's residual).
|
||||
center_panel_header=400,0,620,92
|
||||
npc_dialogue=456,0,30,150
|
||||
bind_skill=516,619,251,29
|
||||
quest_skill_btn=284,455,710,121
|
||||
|
||||
@@ -37,6 +37,7 @@ class HealthManager:
|
||||
self._last_chicken_screenshot = None
|
||||
self._count_panel_detects = 0
|
||||
self._count_wp_panel_detects = 0
|
||||
self._count_center_panel_detects = 0
|
||||
|
||||
def stop_monitor(self):
|
||||
self._do_monitor = False
|
||||
@@ -202,6 +203,27 @@ class HealthManager:
|
||||
self._last_merc_heal = time.time()
|
||||
|
||||
# Close any open panels that might block detection
|
||||
# A CENTRED panel (Chronicle) matches neither header ROI, so
|
||||
# nothing ever closed it — and a centred panel blanks every
|
||||
# later template search. Measured 2026-08-28: Chronicle open,
|
||||
# A5_RED_PORTAL unfindable, 66s approach failure. It is not a
|
||||
# threat, so escape it without counting toward a chicken,
|
||||
# bounded the same way as the waypoint panel.
|
||||
if not self.get_panel_check_paused() and is_visible(ScreenObjects.CenterPanel, img):
|
||||
self._count_center_panel_detects = getattr(self, "_count_center_panel_detects", 0) + 1
|
||||
if self._count_center_panel_detects <= self._MAX_WP_PANEL_ESCAPES:
|
||||
Logger.debug(
|
||||
f"Centred panel open (blocks all template matching) — closing it "
|
||||
f"({self._count_center_panel_detects}/{self._MAX_WP_PANEL_ESCAPES})"
|
||||
)
|
||||
from input_layer import keyboard as kb
|
||||
kb.send("esc")
|
||||
wait(0.1, 0.2)
|
||||
fn_end = time.perf_counter()
|
||||
wait(max(0.01, (1/15 - (fn_end - fn_start)) * random.uniform(0.8, 1.2)))
|
||||
continue
|
||||
Logger.warning("Centred panel would not close — treating as a blocking panel")
|
||||
|
||||
if not self.get_panel_check_paused() and (is_visible(ScreenObjects.LeftPanel, img) or is_visible(ScreenObjects.RightPanel, img)):
|
||||
# A waypoint panel is self-inflicted: the pather walked the char
|
||||
# over the WP stone. That is a navigation problem, not a threat,
|
||||
@@ -237,6 +259,7 @@ class HealthManager:
|
||||
else:
|
||||
# Screen is clear, so any waypoint panel we escaped did close.
|
||||
self._count_wp_panel_detects = 0
|
||||
self._count_center_panel_detects = 0
|
||||
|
||||
fn_end = time.perf_counter()
|
||||
# Target ~15 FPS polling with anti-cheat jitter
|
||||
|
||||
@@ -225,6 +225,12 @@ class ScreenObjects:
|
||||
threshold=0.8,
|
||||
use_grayscale=True
|
||||
)
|
||||
CenterPanel=ScreenObject(
|
||||
ref=["CLOSE_PANEL_2", "CLOSE_PANEL"],
|
||||
roi="center_panel_header",
|
||||
threshold=0.8,
|
||||
use_grayscale=True
|
||||
)
|
||||
NPCDialogue=ScreenObject(
|
||||
ref="NPC_DIALOGUE",
|
||||
roi="npc_dialogue",
|
||||
|
||||
46
test/test_center_panel.py
Normal file
@@ -0,0 +1,46 @@
|
||||
"""A centred panel must be detected and closed.
|
||||
|
||||
Bug 31 fixed the merc-panel keypress that opened CHRONICLE, but noted the real
|
||||
gap was never closed: the health manager's guard matches only LeftPanel /
|
||||
RightPanel, and a centred panel matches neither. Nothing closes it, and a
|
||||
centred panel blanks EVERY later template search.
|
||||
|
||||
Measured 2026-08-28: Chronicle open, A5_RED_PORTAL unfindable, 66s
|
||||
click_red_portal failure. The Chronicle's close button scores 1.000 against
|
||||
CLOSE_PANEL_2 at (952, 56) — inside right_panel_header's x-range but exactly at
|
||||
its 56px height boundary, so the match centre falls outside the ROI.
|
||||
"""
|
||||
import inspect
|
||||
|
||||
|
||||
def test_center_panel_screenobject_exists():
|
||||
from ui_manager import ScreenObjects
|
||||
|
||||
assert hasattr(ScreenObjects, "CenterPanel"), "no detection for a centred panel"
|
||||
|
||||
|
||||
def test_center_panel_roi_covers_the_chronicle_close_button():
|
||||
from config import Config
|
||||
|
||||
x, y, w, h = Config().ui_roi["center_panel_header"]
|
||||
cx, cy = 952, 56 # measured Chronicle X position
|
||||
assert x <= cx <= x + w, "ROI does not span the Chronicle close button in x"
|
||||
assert y <= cy <= y + h, (
|
||||
"ROI does not span it in y — this is exactly how right_panel_header "
|
||||
"missed it, its 56px height ending at the button's centre"
|
||||
)
|
||||
|
||||
|
||||
def test_guard_checks_center_panel_before_chickening():
|
||||
"""It must be escaped, not counted as a threat.
|
||||
|
||||
A centred panel is self-inflicted UI state, like the waypoint panel.
|
||||
Chickening on it throws away a healthy game.
|
||||
"""
|
||||
from health_manager import HealthManager
|
||||
|
||||
src = inspect.getsource(HealthManager)
|
||||
assert "ScreenObjects.CenterPanel" in src, "guard never checks for a centred panel"
|
||||
center_at = src.index("ScreenObjects.CenterPanel")
|
||||
chicken_at = src.index("Chickening to be safe")
|
||||
assert center_at < chicken_at, "centred-panel escape must come before the chicken path"
|
||||