119 lines
4.5 KiB
Python
119 lines
4.5 KiB
Python
"""
|
|
Audit the stash against the CURRENT pickit rules — find slots you can free.
|
|
|
|
Walks every stash page (0 = personal, 1-5 = shared), hovers each occupied
|
|
slot, OCRs the item tooltip, and evaluates it with the same should_keep()
|
|
the bot uses at stash time. Items that no longer match any keep rule (e.g.
|
|
early-season keeps after switching to an endgame pickit profile) are listed
|
|
as FREE-ABLE. Read-only: nothing is moved, sold or dropped.
|
|
|
|
Usage: be in a game with the STASH OPEN (stand at stash, open it), then:
|
|
<botty-env-python> tools/audit_stash_pickit.py [max_page]
|
|
Report goes to console and log/stash_audit_<timestamp>.txt.
|
|
Respects the active profile's pickit (profiles/pickit_profiles resolution).
|
|
"""
|
|
import os
|
|
import sys
|
|
import time
|
|
import ctypes
|
|
import itertools
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
|
|
ctypes.windll.user32.SetProcessDPIAware()
|
|
|
|
from config import Config
|
|
from logger import Logger
|
|
from screen import start_detecting_window, stop_detecting_window, grab, convert_screen_to_monitor
|
|
from utils.misc import wait, cut_roi
|
|
from input_layer import mouse
|
|
from inventory import common
|
|
from ui_manager import is_visible, ScreenObjects
|
|
import d2r_image
|
|
from bnip.actions import should_keep
|
|
|
|
STASH_COLS, STASH_ROWS = 10, 10
|
|
|
|
|
|
def _stash_slot_pos(col: int, row: int) -> tuple[int, int]:
|
|
x, y, _, _ = Config().ui_roi["left_inventory"]
|
|
w, h = Config().ui_pos["slot_width"], Config().ui_pos["slot_height"]
|
|
return (x + col * w + w // 2, y + row * h + h // 2)
|
|
|
|
|
|
def _slot_occupied(img, col: int, row: int) -> bool:
|
|
x, y, _, _ = Config().ui_roi["left_inventory"]
|
|
w, h = Config().ui_pos["slot_width"], Config().ui_pos["slot_height"]
|
|
ox, oy = int(w * 0.12), int(h * 0.12)
|
|
sx, sy = x + col * w, y + row * h
|
|
return common.slot_has_item(img[sy + oy:sy + h - oy, sx + ox:sx + w - ox])
|
|
|
|
|
|
def audit_page(page: int) -> list[dict]:
|
|
common.select_stash_page(page)
|
|
wait(0.5, 0.7)
|
|
img = grab(force_new=True)
|
|
results = []
|
|
seen_uids = set()
|
|
for col, row in itertools.product(range(STASH_COLS), range(STASH_ROWS)):
|
|
if not _slot_occupied(img, col, row):
|
|
continue
|
|
x_m, y_m = convert_screen_to_monitor(_stash_slot_pos(col, row))
|
|
mouse.move(x_m, y_m, randomize=4, delay_factor=[0.15, 0.25])
|
|
wait(0.15, 0.2)
|
|
hovered = grab(force_new=True)
|
|
try:
|
|
props, box = d2r_image.get_hovered_item(hovered)
|
|
except Exception:
|
|
continue
|
|
if box is None or props is None:
|
|
continue
|
|
name = getattr(box, "ocr_result", None)
|
|
name = (name.text.splitlines()[0] if name and name.text else "?").strip()
|
|
uid = name + str(getattr(props, "NTIPAliasType", ""))
|
|
if (name, col // 2, row // 2) in seen_uids: # multi-slot items rehover
|
|
continue
|
|
seen_uids.add((name, col // 2, row // 2))
|
|
try:
|
|
keep, expression = should_keep(props.as_dict())
|
|
except Exception as e:
|
|
keep, expression = None, f"eval failed: {e}"
|
|
results.append({"page": page, "slot": (col, row), "name": name,
|
|
"keep": keep, "rule": expression or ""})
|
|
return results
|
|
|
|
|
|
def main():
|
|
max_page = int(sys.argv[1]) if len(sys.argv) > 1 else 5
|
|
print("Stash must be OPEN in-game. Starting audit in 5s...")
|
|
time.sleep(5)
|
|
start_detecting_window()
|
|
wait(0.5)
|
|
all_results = []
|
|
try:
|
|
if not is_visible(ScreenObjects.GoldBtnStash, grab(force_new=True)):
|
|
raise SystemExit("Stash panel not detected — open the stash first.")
|
|
for page in range(0, max_page + 1):
|
|
page_results = audit_page(page)
|
|
all_results.extend(page_results)
|
|
print(f"page {page}: {len(page_results)} items")
|
|
finally:
|
|
stop_detecting_window()
|
|
|
|
freeable = [r for r in all_results if r["keep"] is False]
|
|
lines = [f"STASH AUDIT vs pickit ({time.strftime('%Y-%m-%d %H:%M')}) — "
|
|
f"{len(all_results)} items, {len(freeable)} FREE-ABLE\n"]
|
|
for r in all_results:
|
|
verdict = {True: "KEEP ", False: "FREE ", None: "UNKNOWN"}[r["keep"]]
|
|
lines.append(f" p{r['page']} {str(r['slot']):>8} {verdict} {r['name']}"
|
|
+ (f" [{r['rule']}]" if r["keep"] else ""))
|
|
report = "\n".join(lines)
|
|
print(report)
|
|
out = f"log/stash_audit_{time.strftime('%Y%m%d_%H%M%S')}.txt"
|
|
with open(out, "w", encoding="utf-8") as f:
|
|
f.write(report)
|
|
print(f"\nreport saved: {out}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|