fix(town): stop burning the NPC search on body+pose guesses; give A3 usable town markers
Measured over the 65-game session of 2026-09-04 (62 successes, 3 failures, all three "Maintenance timeout ~300s before [gamble]"): 1. open_npc_menu's body+pose path is a guess, not a confirmation. For Ormus it opened the dialogue 5/73 times (7%); the name-tag path hit 37/47 (79%). Body score cannot separate them — failures averaged 0.46, the 5 successes were 0.40-0.46, and one false positive scored 0.79 — so there is nothing to threshold on. Each guess also paid a retry click plus two 2.5s waits. 68 dead clicks is ~8 minutes of town time per session and is what drove the timeouts. Guesses are now budgeted (2 per call) and no longer retried, so the search reaches the grid sweep, which does find the NPC, with budget left to do it. 2. TOWN_MARKERS carried A3_TOWN_0 and A3_TOWN_1 for Act 3. Against 8 Kurast Docks screenshots they score 0.21 and 0.29 — the worst two of all 20 a3_town templates, against a 0.68 match threshold. Act detection in A3 therefore could not succeed: 35 "no town marker found" and 24 "_verify_town_location: act detection failed" in one session, which is the Bug 9 desync trigger. Added A3_TOWN_14 (0.95) and A3_TOWN_20 (0.74); both are act-unique, scoring <=0.35 against A4 and A5 frames, so they cannot misfire. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
914d4a911e
commit
89a4d86e2d
+18
-3
@@ -318,6 +318,9 @@ def open_npc_menu(
|
||||
# Search for npc name tags by hovering to all template locations that are found
|
||||
start = time.time()
|
||||
attempts = 0
|
||||
# Clicks taken on a body+pose guess rather than a confirmed name tag.
|
||||
weak_attempts = 0
|
||||
MAX_WEAK_ATTEMPTS = 2
|
||||
while (time.time() - start) < NPC_SEARCH_TIMEOUT:
|
||||
img = grab()
|
||||
# Fast path: if the target NPC's dialogue is already open, return immediately.
|
||||
@@ -405,7 +408,17 @@ def open_npc_menu(
|
||||
if name_tag_confirmed:
|
||||
Logger.info(f"Clicking on {npc_key} at {mouse.get_position()} (name tag confirmed)")
|
||||
elif body_confident and pose_confirmed:
|
||||
Logger.info(f"Clicking on {npc_key} at {mouse.get_position()} (body+pose confirmed, name tag score {res_w.score:.3f})")
|
||||
# Body+pose is a guess, not a confirmation: over the 2026-09-04 session it
|
||||
# opened the dialogue 5/73 times for Ormus (7%) while the name-tag path hit
|
||||
# 37/47 (79%). Body score does not separate the two (failures averaged 0.46,
|
||||
# successes 0.40-0.46, one false positive scored 0.79), so there is nothing
|
||||
# to threshold on — the only useful lever is to stop spending the search
|
||||
# window on guesses and reach the grid sweep, which does find the NPC.
|
||||
if weak_attempts >= MAX_WEAK_ATTEMPTS:
|
||||
Logger.debug(f"NPC {npc_key} - body+pose guess budget spent, leaving the rest to the grid sweep")
|
||||
break
|
||||
weak_attempts += 1
|
||||
Logger.info(f"Clicking on {npc_key} at {mouse.get_position()} (body+pose guess {weak_attempts}/{MAX_WEAK_ATTEMPTS}, name tag score {res_w.score:.3f})")
|
||||
else:
|
||||
Logger.debug(f"NPC {npc_key} - skipping pos {result['pos']}: name_tag={name_tag_confirmed} in_roi={in_npc_roi} body={body_confident} pose={pose_confirmed}")
|
||||
continue
|
||||
@@ -421,8 +434,10 @@ def open_npc_menu(
|
||||
if _wait_action_btns(npc_key, timeout=2.5):
|
||||
Logger.debug(f"NPC {npc_key} - dialogue open (action buttons visible)")
|
||||
return True
|
||||
# Retry once: first click sometimes just turns the NPC toward you.
|
||||
if attempts <= 2:
|
||||
# Retry once: first click sometimes just turns the NPC toward you. Only worth
|
||||
# doing when the name tag confirmed we are actually on the NPC — retrying a
|
||||
# body+pose guess just pays the click and the 2.5s wait twice for nothing.
|
||||
if attempts <= 2 and name_tag_confirmed:
|
||||
Logger.debug(f"NPC {npc_key} - action buttons not found, retrying click on body")
|
||||
mouse.move(*result["pos"], randomize=3, delay_factor=[0.2, 0.3])
|
||||
wait(0.2, 0.3)
|
||||
|
||||
@@ -14,7 +14,11 @@ from inventory import personal, vendor, common
|
||||
TOWN_MARKERS = [
|
||||
"A5_TOWN_0", "A5_TOWN_1", "A5_TOWN_5", "A5_TOWN_6",
|
||||
"A4_TOWN_4", "A4_TOWN_5",
|
||||
"A3_TOWN_0", "A3_TOWN_1",
|
||||
# A3_TOWN_14 / _20 carry Act 3: measured against 8 Kurast Docks screenshots
|
||||
# they hit 0.95 / 0.74, while A3_TOWN_0 and _1 managed 0.21 and 0.29 — the two
|
||||
# worst of all 20 a3_town templates, i.e. act detection in A3 could not succeed.
|
||||
# Both are act-unique (<=0.35 against A4 and A5 frames), so they cannot misfire.
|
||||
"A3_TOWN_14", "A3_TOWN_20", "A3_TOWN_0", "A3_TOWN_1",
|
||||
"A2_TOWN_0", "A2_TOWN_1", "A2_TOWN_10",
|
||||
"A1_TOWN_1", "A1_TOWN_3"
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user