v0.2.0: relations preview, item shop, abilities, overlay recommend, GSI enhancements
- Add relations/item/abilities preview (serve_relations.py + web/relations/) - Add fetch scripts: hero_items, item_shop, items_meta, hero_abilities, ability_videos, patches, stratz, matchups, portraits - Add overlay.py (role tags + Top-3 cyan marks), recommend.py - Add http_utils.py, loc_format.py, hero_tags.py, item_fears.py - GSI: full payload JSONL dump, foreground window detection - Drop real template library; CDN-only matching - Update docs: CHANGELOG 0.2.0, DESIGN config table, AGENTS module table - .gitignore: exclude large regenerable assets (icons/portraits/videos)
This commit is contained in:
+21
-4
@@ -33,6 +33,7 @@ import mss
|
||||
from capture import append_gsi_payload, grab_frame, is_dota_foreground, raw_dir_for_match, save_frame
|
||||
from common import ROOT, load_config, load_template_library
|
||||
from draft_session import HERO_SELECTION, DraftSession, describe, gsi_slot, loc
|
||||
from overlay import DraftOverlay
|
||||
from recognize import recognize_image
|
||||
from roles import detect_roles
|
||||
|
||||
@@ -68,6 +69,15 @@ class Watcher:
|
||||
self.dump_lock = threading.Lock()
|
||||
self.self_info: dict = {}
|
||||
self._dump_announced: set[str] = set()
|
||||
# One overlay for the whole process - Tk does not like create/destroy per match.
|
||||
self.overlay = None
|
||||
if calibrated and bool((cfg.get("overlay") or {}).get("enabled", True)):
|
||||
try:
|
||||
self.overlay = DraftOverlay(cfg)
|
||||
print("[draft] role-tag overlay ready", flush=True)
|
||||
except Exception as e: # noqa: BLE001
|
||||
print(f"[draft] overlay disabled: {e}", flush=True)
|
||||
self.overlay = None
|
||||
|
||||
def on_payload(self, payload: dict) -> None:
|
||||
if not self.connected:
|
||||
@@ -82,6 +92,8 @@ class Watcher:
|
||||
"team": p.get("team_name"),
|
||||
"team_slot": p.get("team_slot"),
|
||||
"hero": (h.get("name") or "").replace("npc_dota_hero_", "") or None,
|
||||
"accountid": p.get("accountid"),
|
||||
"steamid": p.get("steamid"),
|
||||
}
|
||||
|
||||
state = (payload.get("map") or {}).get("game_state")
|
||||
@@ -142,9 +154,9 @@ class Watcher:
|
||||
|
||||
late = " (joined late)" if state != HERO_SELECTION else ""
|
||||
print(f"[draft] tracking match {match_id} from {state}{late}", flush=True)
|
||||
session = DraftSession(self.cfg, self.library)
|
||||
session = DraftSession(self.cfg, self.library, overlay=self.overlay)
|
||||
summary = session.run(match_id, lambda: self.last_state, lambda: self.self_info)
|
||||
if summary["recognized"] == 0:
|
||||
if not summary or summary["recognized"] == 0:
|
||||
print("[draft] nothing recognized - no result written", flush=True)
|
||||
return None
|
||||
self.report_session(summary)
|
||||
@@ -207,11 +219,14 @@ class Watcher:
|
||||
print(f"[run] {result['recognized']}/10 slots, {result['elapsed_ms']}ms", flush=True)
|
||||
print(f"radiant: {', '.join(loc(r['hero']) for r in result['radiant'])}", flush=True)
|
||||
print(f"dire : {', '.join(loc(r['hero']) for r in result['dire'])}", flush=True)
|
||||
slot = gsi_slot(self.self_info) or found["self_slot"]
|
||||
slot = gsi_slot(self.self_info)
|
||||
team = self.self_info.get("team") or found.get("self_team")
|
||||
if slot:
|
||||
role = found["roles"].get(slot)
|
||||
pos = f"position {role['position']} ({role['label']})" if role else "position unknown"
|
||||
print(f"you : slot {slot} {found['self_team']} - {pos}", flush=True)
|
||||
print(f"you : slot {slot} {team} - {pos}", flush=True)
|
||||
elif not slot:
|
||||
print("you : slot unknown (waiting for GSI team_slot)", flush=True)
|
||||
result["roles"] = found
|
||||
return result
|
||||
|
||||
@@ -306,6 +321,8 @@ def main() -> None:
|
||||
except KeyboardInterrupt:
|
||||
print("\nstopped.")
|
||||
finally:
|
||||
if watcher.overlay is not None:
|
||||
watcher.overlay.close()
|
||||
server.server_close()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user