feat: pather auto-recovery sweep when a node is lost

traverse_nodes previously gave up after one random-guess move when a node
template wasn't on screen, failing every travel path where the char was
mis-positioned (the A5-after-Pindle stranding was the worst case). Now,
on timeout during travel pathing (timeout > 3.1s, so boss approaches keep
their fast fail), it walks an expanding 12-step sweep, re-scanning for the
node each step, and only fails once the sweep is exhausted. Makes ALL
node pathing self-healing, not just A5.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
alex
2026-06-10 20:51:20 +02:00
parent 19011c1a12
commit f1c5b6cdf4

View File

@@ -674,12 +674,24 @@ class Pather:
# we either want to tele charge but have no charges or don't wanna use the charge falling back to default pre_move handling
char.pre_move()
# Auto-recovery sweep directions (absolute coords, char-centred). When a node
# template can't be found within `timeout`, the char is likely mis-positioned
# (e.g. returned from a boss to a town corner). Rather than fail immediately,
# walk this expanding pattern — each move brings new screen area into view so
# find_abs_node_pos can re-acquire the node. Only used for travel pathing
# (timeout > 3.1); boss approaches keep their fast fail so combat isn't stalled.
_recovery_sweep = [
(-300, 0), (300, 0), (0, -250), (0, 250),
(-350, -250), (350, 250), (350, -250), (-350, 250),
(-200, 0), (0, -300), (200, 0), (0, 300),
]
last_direction = None
for i, node_idx in enumerate(path):
continue_to_next_node = False
last_move = time.time()
did_force_move = False
teleport_count = 0
recovery_idx = 0
while not continue_to_next_node:
img = grab(force_new=True)
# Handle timeout
@@ -689,6 +701,19 @@ class Pather:
Logger.debug("Opened wp, closing it again")
keyboard.send("esc")
last_move = time.time()
elif timeout > 3.1 and recovery_idx < len(_recovery_sweep):
# Auto-recover: actively explore to bring the node back on screen
# instead of giving up. Each step re-scans in the main loop below.
rec_abs = get_closest_non_hud_pixel(pos=_recovery_sweep[recovery_idx], pos_type="abs")
Logger.warning(
f"Pather: node {node_idx} lost — auto-recovery sweep "
f"{recovery_idx + 1}/{len(_recovery_sweep)} towards {rec_abs}"
)
x_m, y_m = convert_abs_to_monitor(rec_abs)
char.move((x_m, y_m), force_move=True)
recovery_idx += 1
did_force_move = True
last_move = time.time()
else:
# This is a bit hacky, but for moving into a boss location we set timeout usually quite low
# because of all the spells and monsters it often can not determine the final template
@@ -696,7 +721,7 @@ class Pather:
if timeout > 3.1:
if Config().general["info_screenshots"]:
safe_imwrite("./log/screenshots/info/info_pather_got_stuck_" + time.strftime("%Y%m%d_%H%M%S") + ".png", img)
Logger.error("Got stuck exit pather")
Logger.error(f"Got stuck exit pather (node {node_idx}, recovery sweep exhausted)")
return False
# Sometimes we get stuck at rocks and stuff, after a few seconds force a move into the last known direction