feat: grid hover sweep fallback when NPC body templates fail

Body templates went stale with the June 2026 patch (Malah, Cain,
Tyrael, Larzuk, Qual-Kehk all undetectable), but name tags on hover
score 0.9+ reliably. When the body-template hunt times out, sweep a
coarse hover grid over the NPC ROI and click wherever the name tag
appears.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
alex
2026-06-10 18:00:44 +02:00
parent d9a6e7ce40
commit f410ea21f1

View File

@@ -423,6 +423,42 @@ def open_npc_menu(npc_key: Npc) -> bool:
else:
Logger.warning(f"NPC {npc_key} - clicked but no action buttons found")
wait(0.1) # Brief pause before next frame grab if no matches were valid
# FINAL FALLBACK: body templates can go stale after a game patch (June 2026
# patch changed several NPC appearances), but the name-tag-on-hover check is
# rendering-stable — a real hover scores 0.9+. Sweep a coarse hover grid over
# the NPC's ROI and click wherever the name tag appears.
from screen import convert_screen_to_monitor
Logger.debug(f"NPC {npc_key} - body-template hunt timed out, starting grid hover sweep")
_close_open_panels()
sweep_roi = npcs[npc_key]["roi"] if "roi" in npcs[npc_key] else roi_npc_search
sx0, sy0, sw, sh = sweep_roi
SWEEP_TAG_THRESHOLD = 0.4
for sy in range(int(sy0 + 30), int(sy0 + sh - 15), 70):
for sx in range(int(sx0 + 30), int(sx0 + sw - 15), 60):
pos_m = convert_screen_to_monitor((sx, sy))
mouse.move(*pos_m, randomize=2, delay_factor=[0.05, 0.1])
wait(0.10, 0.14)
hov = grab()
tag_roi = [max(0, sx - 120), max(0, sy - 160), 240, 140]
_, f_w = color_filter(hov, Config().colors["white"])
res_w = template_finder.search(npcs[npc_key]["name_tag_white"], f_w, SWEEP_TAG_THRESHOLD, roi=tag_roi)
if not res_w.valid:
res_w = template_finder.search(npcs[npc_key]["name_tag_white"], hov, SWEEP_TAG_THRESHOLD, roi=tag_roi, use_grayscale=True)
if res_w.valid:
Logger.info(f"NPC {npc_key} - grid sweep found name tag at ({sx}, {sy}) (score {res_w.score:.3f}), clicking")
mouse.click(button="left")
wait(0.5, 0.7)
if _wait_action_btns(npc_key, timeout=2.5):
Logger.info(f"NPC {npc_key} - dialogue open via grid sweep")
return True
# one retry: first click sometimes just turns the NPC
mouse.move(*pos_m, randomize=2, delay_factor=[0.1, 0.15])
wait(0.15, 0.2)
mouse.click(button="left")
wait(0.5, 0.7)
if _wait_action_btns(npc_key, timeout=2.5):
Logger.info(f"NPC {npc_key} - dialogue open via grid sweep (retry)")
return True
Logger.error(f"open_npc_menu: timed out finding {npc_key} — saving screenshot")
if Config().general["info_screenshots"]:
safe_imwrite("./log/screenshots/info/info_npc_menu_timeout_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab())