diff --git a/src/town/a5.py b/src/town/a5.py index d1812e5..f42ac57 100644 --- a/src/town/a5.py +++ b/src/town/a5.py @@ -127,10 +127,26 @@ class A5(IAct): return True # Recovery path after failed vendor/menu interactions: restart from known town start. Logger.warning("A5 waypoint open failed, retrying from town start anchor") - if not self._pather.traverse_nodes((Location.A5_TOWN_START, Location.A5_WP), self._char, force_move=True): - return False - wait(0.6, 0.8) - return self._char.select_by_template("A5_WP", found_wp_func, telekinesis=True) + if self._pather.traverse_nodes((Location.A5_TOWN_START, Location.A5_WP), self._char, force_move=True): + wait(0.6, 0.8) + if self._char.select_by_template("A5_WP", found_wp_func, telekinesis=True): + return True + # Last resort: the char may be stranded in a corner where no path nodes anchor + # (e.g. after a failed Malah hunt). Walk a search pattern across the small town + # and scan the whole screen for the WP template directly each step. + from screen import convert_abs_to_monitor + Logger.warning("A5 open_wp: node pathing failed — walking search pattern with direct WP scan") + for step_abs in [(-350, 100), (-300, -200), (-250, 250), (450, 50), (-100, -300)]: + wp_match = template_finder.search("A5_WP", grab(), threshold=0.58) + if wp_match.valid: + self._char.move(wp_match.center_monitor, force_move=True) + wait(0.5, 0.7) + if self._char.select_by_template("A5_WP", found_wp_func, threshold=0.58, telekinesis=True): + return True + pos_m = convert_abs_to_monitor(step_abs) + self._char.move(pos_m, force_move=True) + wait(1.2, 1.5) + return False def wait_for_tp(self) -> Location | bool: success = template_finder.search_and_wait(["A5_TOWN_1", "A5_TOWN_0"], timeout=20).valid