#!/usr/bin/env python3 """ Render every classic dungeon's first-floor webp with kg's enemy positions overlaid as colored circles, plus pack polygon outlines, to verify alignment visually. Output: /tmp/kg_alignment/.png """ from __future__ import annotations import json import sys from pathlib import Path from PIL import Image, ImageDraw ROOT = Path(__file__).resolve().parent.parent WEB_MAPS = ROOT / "web" / "assets" / "maps" DUNGEONS_JSON = ROOT / "web" / "assets" / "dungeons.json" OUT_DIR = Path("/tmp/kg_alignment") CLASS_RADIUS = {1: 14, 2: 18, 3: 28, 4: 38} CLASS_FILL = {1: "#9aa1aa", 2: "#d6d6dc", 3: "#d63b3b", 4: "#ffd83a"} def main() -> int: OUT_DIR.mkdir(parents=True, exist_ok=True) data = json.loads(DUNGEONS_JSON.read_text()) rendered = [] for d in data["dungeons"]: if not d["maps"]: continue m = d["maps"][0] src = WEB_MAPS / Path(m["image"]).name if not src.exists(): continue with Image.open(src) as im: im = im.convert("RGBA") W, H = im.size draw = ImageDraw.Draw(im) for p in m.get("packs", []): pts = [(int(x), int(y)) for x, y in p["vertices"]] if len(pts) >= 3: draw.polygon(pts, outline=p["color"], width=4) for e in m.get("enemies", []): cls = e.get("classification") or 1 r = CLASS_RADIUS.get(cls, 14) fill = CLASS_FILL.get(cls, "#9aa1aa") x, y = int(e["pos"][0]), int(e["pos"][1]) draw.ellipse([x - r, y - r, x + r, y + r], fill=fill, outline="black", width=3) scale = 1024 / W small = im.resize((1024, int(H * scale)), Image.LANCZOS) out = OUT_DIR / f"{d['id']}.png" small.save(out) rendered.append((d["id"], d["name"], len(m.get("enemies", [])), len(m.get("packs", [])))) print(f"rendered {len(rendered)} dungeons → {OUT_DIR}") for did, name, n_e, n_p in rendered: print(f" {did:32s} enemies={n_e:4d} packs={n_p:3d}") return 0 if __name__ == "__main__": raise SystemExit(main())