From f410ea21f12abe5eba0091f0a20eb413df20ec3c Mon Sep 17 00:00:00 2001 From: alex Date: Wed, 10 Jun 2026 18:00:44 +0200 Subject: [PATCH] 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 --- src/npc_manager.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/npc_manager.py b/src/npc_manager.py index 618d354..685da1a 100644 --- a/src/npc_manager.py +++ b/src/npc_manager.py @@ -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())