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:
alexpolo1
2026-09-04 09:46:11 +02:00
co-authored by Claude Opus 5
parent 914d4a911e
commit 89a4d86e2d
2 changed files with 23 additions and 4 deletions
+18 -3
View File
@@ -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)
+5 -1
View File
@@ -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"
]