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:
@@ -1,11 +1,7 @@
|
||||
"""Read the role-queue labels and find which top-bar slot is you.
|
||||
"""Read role-queue lane labels under top-bar portraits.
|
||||
|
||||
Two things live in the strip of text under each top-bar portrait:
|
||||
|
||||
row 1 (name) - your own name renders white and bold, everyone else's is
|
||||
tinted blue, which is enough to tell which slot is you.
|
||||
row 2 (role) - only drawn for your own team, and only in role-queue
|
||||
(定位匹配) matches: 优势路 / 中路 / 劣势路 / 辅助 / 纯辅助.
|
||||
Row under each portrait (role-queue only, own team): 优势路 / 中路 / 劣势路 /
|
||||
辅助 / 纯辅助. Your own slot index comes from GSI team_slot, not from name tint.
|
||||
|
||||
The role text is flat grey with zero saturation, so a threshold on
|
||||
value+saturation isolates it cleanly. Matching is done on the binary mask
|
||||
@@ -118,9 +114,11 @@ def load_role_templates() -> dict[str, np.ndarray]:
|
||||
|
||||
|
||||
def detect_roles(img: np.ndarray, cfg: dict, templates: dict[str, np.ndarray] | None = None) -> dict:
|
||||
"""Per-slot role plus which slot is you.
|
||||
"""Per-slot role labels from the role-queue text under top-bar portraits.
|
||||
|
||||
Returns {"self_slot": int|None, "self_team": str|None, "roles": {slot: {...}}}.
|
||||
Returns {"self_team": str|None, "roles": {slot: {...}}}.
|
||||
Your own top-bar slot comes from GSI team_slot elsewhere - this helper
|
||||
does not guess it from name brightness.
|
||||
Slots without a role label (the enemy team, or any non-role-queue mode)
|
||||
are simply absent from "roles".
|
||||
"""
|
||||
@@ -144,38 +142,11 @@ def detect_roles(img: np.ndarray, cfg: dict, templates: dict[str, np.ndarray] |
|
||||
"score": round(score, 3),
|
||||
}
|
||||
|
||||
self_slot = _detect_self(img, cfg)
|
||||
self_team = None
|
||||
if roles:
|
||||
self_team = "radiant" if min(roles) <= 5 else "dire"
|
||||
elif self_slot is not None:
|
||||
self_team = "radiant" if self_slot <= 5 else "dire"
|
||||
|
||||
return {"self_slot": self_slot, "self_team": self_team, "roles": roles}
|
||||
|
||||
|
||||
def _detect_self(img: np.ndarray, cfg: dict) -> int | None:
|
||||
"""Your own name is drawn bright white, the other nine a dimmer blue-grey.
|
||||
|
||||
The gap is ~55 units of brightness, so compare slots against each other
|
||||
instead of a fixed threshold - that survives HUD skins and any screen
|
||||
where every row happens to be bright (a menu, a loading overlay), because
|
||||
there the runner-up is just as bright and the match is rejected.
|
||||
"""
|
||||
r = cfg.get("roles", {})
|
||||
min_value = r.get("self_min_value", 195.0)
|
||||
min_gap = r.get("self_min_gap", 25.0)
|
||||
found = []
|
||||
for slot in cfg.get("slots", []):
|
||||
tint = name_tint(img, slot, cfg)
|
||||
if tint is not None:
|
||||
found.append((tint[0], slot["index"]))
|
||||
if len(found) < 2:
|
||||
return None
|
||||
found.sort(reverse=True)
|
||||
if found[0][0] < min_value or found[0][0] - found[1][0] < min_gap:
|
||||
return None
|
||||
return found[0][1]
|
||||
return {"self_team": self_team, "roles": roles}
|
||||
|
||||
|
||||
def _main() -> None:
|
||||
|
||||
Reference in New Issue
Block a user