fix(char): move() clicked into the HUD and toggled the loot filter
Reported after Larzuk trips: the bot pressed escape, then flipped the loot
filter. Larzuk stands on the left of Harrogath, so moves to and from him aim at
the bottom-left corner — where D2R puts the seven loot-filter category buttons
(screen x 395-560, y 692-712). A right-click there toggles a filter, which
changes what renders and therefore what every later template match can see.
IChar.move() applied NO HUD avoidance in either branch:
# teleport
mouse.move(pos_monitor[0], pos_monitor[1], randomize=3, ...)
mouse.click(button="right")
# walk
x, y = convert_abs_to_monitor(pos_abs)
mouse.move(x, y, randomize=5, ...)
The pather's anti-stuck path already called get_closest_non_hud_pixel; move()
never did, and assets/hud_mask.png covers those buttons correctly — the mask
was simply not consulted.
Latent for a long time and surfaced by Enigma. The walk branch shrinks its
target toward centre via adjust_factor, which mostly kept clicks off the HUD by
accident; the teleport branch clicks the raw target, so any low aim point lands
on the interface.
The escape that precedes it is unrelated and correct — common.close() dismissing
the repair panel.
Verified: filter-row targets are moved from y=700 to y=508, a centre target is
returned unchanged.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
74c3050163
commit
91e201c5f6
@@ -232,6 +232,18 @@ class IChar:
|
||||
Logger.error("timeout waiting for tele skill to activate")
|
||||
|
||||
def move(self, pos_monitor: tuple[float, float], force_tp: bool = False, force_move: bool = False):
|
||||
# Never click into the HUD. Neither branch below did this, so a move
|
||||
# target low on the screen landed on the interface — most visibly the
|
||||
# loot-filter toggles at the bottom-left (screen x 395-560, y 692-712),
|
||||
# which a right-click flips. Reported after Larzuk trips: he stands on
|
||||
# the left of Harrogath, so moves to and from him aim at that corner.
|
||||
#
|
||||
# Latent for a long time and surfaced by Enigma: the walk branch shrinks
|
||||
# its target toward centre via adjust_factor, which mostly kept clicks
|
||||
# off the HUD by accident, while the teleport branch clicks the raw
|
||||
# target. The pather's anti-stuck path already guarded this; move() did
|
||||
# not.
|
||||
pos_monitor = get_closest_non_hud_pixel(pos_monitor, "monitor")
|
||||
factor = Config().advanced_options["pathing_delay_factor"]
|
||||
if "teleport" in Config().char and Config().char["teleport"] and (
|
||||
force_tp
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
"""move() must never click into the HUD.
|
||||
|
||||
Reported 2026-08-28: after a Larzuk trip the bot pressed escape and then
|
||||
toggled the LOOT FILTER. Larzuk stands on the left of Harrogath, so moves to
|
||||
and from him aim at the bottom-left corner — where D2R puts the seven
|
||||
loot-filter category buttons (screen x 395-560, y 692-712). A right-click there
|
||||
flips a filter, which changes what renders and therefore what every later
|
||||
template match can see.
|
||||
|
||||
IChar.move() applied no HUD avoidance in either branch. The pather's anti-stuck
|
||||
path already called get_closest_non_hud_pixel; move() did not.
|
||||
|
||||
Latent for a long time and surfaced by Enigma: the walk branch shrinks its
|
||||
target toward centre via adjust_factor, which mostly kept clicks off the HUD by
|
||||
accident, while the teleport branch clicks the raw target.
|
||||
"""
|
||||
import inspect
|
||||
|
||||
|
||||
FILTER_ROW = [(400, 700), (470, 700), (550, 700), (470, 710)]
|
||||
|
||||
|
||||
def test_hud_mask_covers_the_loot_filter_buttons():
|
||||
import cv2
|
||||
mask = cv2.imread("assets/hud_mask.png", cv2.IMREAD_GRAYSCALE)
|
||||
mask = cv2.threshold(mask, 1, 255, cv2.THRESH_BINARY)[1]
|
||||
for x, y in FILTER_ROW:
|
||||
assert mask[y, x] == 0, f"loot-filter button ({x},{y}) is not masked as HUD"
|
||||
|
||||
|
||||
def test_targets_on_the_filter_row_are_moved_off_it():
|
||||
from ui_manager import get_closest_non_hud_pixel
|
||||
for x, y in FILTER_ROW:
|
||||
ox, oy = get_closest_non_hud_pixel((x + 5, y + 98), "monitor") # monitor offset
|
||||
assert (oy - 98) < 690, f"({x},{y}) still lands on the HUD at y={oy - 98}"
|
||||
|
||||
|
||||
def test_a_centre_target_is_left_alone():
|
||||
"""The guard must not perturb ordinary targets."""
|
||||
from ui_manager import get_closest_non_hud_pixel
|
||||
assert get_closest_non_hud_pixel((645, 458), "monitor") == (645, 458)
|
||||
|
||||
|
||||
def test_move_applies_the_guard():
|
||||
from char.i_char import IChar
|
||||
src = inspect.getsource(IChar.move)
|
||||
assert "get_closest_non_hud_pixel" in src, "move() can still click into the HUD"
|
||||
guard_at = src.index("get_closest_non_hud_pixel")
|
||||
first_click = min([i for i in (src.find("mouse.move"), src.find("mouse.click")) if i != -1])
|
||||
assert guard_at < first_click, "the guard must run before any click"
|
||||
Reference in New Issue
Block a user