Add 7.41e patch notes with a hand-authored reading sidebar.
Ship Climperor Web 0.6.13: latest-patch summary panel, icon/innate fallbacks, and layout/scroll fixes; keep shared ability badges in git. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+35
-1
@@ -147,6 +147,7 @@ HERO_ABILITIES_PATH = DATA / "hero_abilities.json"
|
||||
ITEM_SHOP_PATH = DATA / "item_shop.json"
|
||||
ITEMS_META_PATH = DATA / "items_meta.json"
|
||||
PATCHES_PATH = DATA / "patches.json"
|
||||
PATCH_SUMMARIES_PATH = DATA / "patch_summaries.json"
|
||||
LEADERBOARDS_PATH = DATA / "leaderboards.json"
|
||||
PRO_MATCHES_PATH = DATA / "pro_matches.json"
|
||||
STREAMERS_PATH = DATA / "streamers.json"
|
||||
@@ -346,6 +347,32 @@ def load_patches() -> dict:
|
||||
}
|
||||
|
||||
|
||||
def load_patch_summaries() -> dict:
|
||||
"""Hand-authored patch readings (see web/data/patch_summaries.json).
|
||||
|
||||
Returns {by_version: {version: summary}}; empty when missing/invalid.
|
||||
Only the latest version is expected to have an entry.
|
||||
"""
|
||||
empty: dict = {"by_version": {}}
|
||||
if not PATCH_SUMMARIES_PATH.is_file():
|
||||
return empty
|
||||
try:
|
||||
raw = json.loads(PATCH_SUMMARIES_PATH.read_text(encoding="utf-8"))
|
||||
except (OSError, json.JSONDecodeError):
|
||||
return empty
|
||||
if not isinstance(raw, dict):
|
||||
return empty
|
||||
by_version = raw.get("by_version")
|
||||
if not isinstance(by_version, dict):
|
||||
return empty
|
||||
out: dict[str, dict] = {}
|
||||
for ver, cell in by_version.items():
|
||||
if not isinstance(ver, str) or not isinstance(cell, dict):
|
||||
continue
|
||||
out[ver] = cell
|
||||
return {"by_version": out}
|
||||
|
||||
|
||||
def load_leaderboards() -> dict:
|
||||
"""Valve Immortal division Top 100 (see fetch_leaderboards.py). Preview only."""
|
||||
empty: dict = {
|
||||
@@ -627,10 +654,14 @@ def ensure_ability_icon(key: str) -> Path | None:
|
||||
try:
|
||||
data = http_bytes(ABILITY_ICON_URL.format(key=key), timeout=30)
|
||||
if not data or len(data) < 32:
|
||||
return None
|
||||
raise OSError("empty ability icon")
|
||||
dest.write_bytes(data)
|
||||
return dest
|
||||
except (urllib.error.URLError, TimeoutError, OSError):
|
||||
# CDN 404s are common for innates; serve the bundled badge when present.
|
||||
innate = ABILITY_ICONS / f"{INNATE_ICON_NAME}.png"
|
||||
if innate.is_file() and innate.stat().st_size >= 32:
|
||||
return innate
|
||||
return None
|
||||
|
||||
|
||||
@@ -724,6 +755,7 @@ def build_payload() -> dict:
|
||||
shop = load_item_shop()
|
||||
items_meta = load_items_meta_index()
|
||||
patches_data = load_patches()
|
||||
patch_summaries = load_patch_summaries()
|
||||
leaderboards = load_leaderboards()
|
||||
pro_matches = load_pro_matches()
|
||||
streamers = load_streamers()
|
||||
@@ -759,6 +791,7 @@ def build_payload() -> dict:
|
||||
"patches": patches_data.get("patches") or [],
|
||||
"patch_lookup": patches_data.get("lookup") or {},
|
||||
"patch_details": patches_data.get("details") or {},
|
||||
"patch_summaries": patch_summaries,
|
||||
"leaderboards": leaderboards,
|
||||
"pro_matches": pro_matches,
|
||||
"streamers": streamers,
|
||||
@@ -801,6 +834,7 @@ def build_payload() -> dict:
|
||||
"shop_items": len(shop.get("items") or {}),
|
||||
"patches": len(patches_data.get("patches") or []),
|
||||
"patch_details": len(patches_data.get("details") or {}),
|
||||
"patch_summaries": len(patch_summaries.get("by_version") or {}),
|
||||
"leaderboard_regions": len(leaderboards.get("regions") or {}),
|
||||
"pro_match_players": len(pro_matches.get("by_pro") or {}),
|
||||
"pro_match_heroes": len(pro_matches.get("by_hero") or {}),
|
||||
|
||||
Reference in New Issue
Block a user