Approve Shu infantry handpainted sprite v8
This commit is contained in:
219
scripts/write-unit-sprite-batch-queue.py
Normal file
219
scripts/write-unit-sprite-batch-queue.py
Normal file
@@ -0,0 +1,219 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
DOCS_DIR = ROOT / "docs"
|
||||
MANIFEST_PATH = DOCS_DIR / "unit-sprite-rework-manifest.json"
|
||||
AUDIT_PATH = DOCS_DIR / "unit-sprite-rework-audit.json"
|
||||
MARKDOWN_PATH = DOCS_DIR / "unit-sprite-batch-queue.md"
|
||||
JSON_PATH = DOCS_DIR / "unit-sprite-batch-queue.json"
|
||||
|
||||
|
||||
BATCH_ORDER = [
|
||||
"batch1-approved-core",
|
||||
"batch1-approved-rebel",
|
||||
"batch2-shu",
|
||||
"batch3-wei",
|
||||
"batch4-wu",
|
||||
"batch5-rebel-other",
|
||||
"batch6-nanman-special",
|
||||
"batch7-unique-officer",
|
||||
]
|
||||
|
||||
BATCH_LABELS = {
|
||||
"batch1-approved-core": "Batch 1 approved core heroes",
|
||||
"batch1-approved-rebel": "Batch 1 approved rebel references",
|
||||
"batch2-shu": "Batch 2 Shu generic units",
|
||||
"batch3-wei": "Batch 3 Wei generic units",
|
||||
"batch4-wu": "Batch 4 Wu generic units",
|
||||
"batch5-rebel-other": "Batch 5 remaining rebel/bandit units",
|
||||
"batch6-nanman-special": "Batch 6 Nanman/special units",
|
||||
"batch7-unique-officer": "Batch 7 unique officers",
|
||||
}
|
||||
|
||||
SPEC_BY_STEM = {
|
||||
"unit-shu-spearman": "unit-sprite-batch2-shu-spearman-spec.md",
|
||||
"unit-shu-archer": "unit-sprite-batch2-shu-archer-spec.md",
|
||||
"unit-shu-cavalry": "unit-sprite-batch2-shu-cavalry-spec.md",
|
||||
"unit-shu-strategist": "unit-sprite-batch2-shu-strategist-spec.md",
|
||||
"unit-shu-officer": "unit-sprite-batch2-shu-officer-spec.md",
|
||||
}
|
||||
|
||||
|
||||
def load_json(path: Path) -> dict[str, Any]:
|
||||
if not path.exists():
|
||||
raise FileNotFoundError(f"missing required file: {path.relative_to(ROOT)}")
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def row_sort_key(row: dict[str, Any], manifest: dict[str, Any]) -> tuple[int, int, str]:
|
||||
stem = row["stem"]
|
||||
pending = manifest.get("pendingApproval", [])
|
||||
next_after = manifest.get("nextAfterApproval", [])
|
||||
if stem in pending:
|
||||
return (0, pending.index(stem), stem)
|
||||
if stem in next_after:
|
||||
return (1, next_after.index(stem), stem)
|
||||
if row.get("review_stage") == "approved-baseline":
|
||||
return (3, 0, stem)
|
||||
return (2, 0, stem)
|
||||
|
||||
|
||||
def summarize_status(row: dict[str, Any]) -> str:
|
||||
base = row["base"]["status"]
|
||||
actions = row["actions"]["status"]
|
||||
if base == "ok" and actions == "ok":
|
||||
return "technical ok"
|
||||
return f"base {base}; actions {actions}"
|
||||
|
||||
|
||||
def queue_data(manifest: dict[str, Any], audit: dict[str, Any]) -> dict[str, Any]:
|
||||
rows = audit.get("rows", [])
|
||||
batches: list[dict[str, Any]] = []
|
||||
pending = set(manifest.get("pendingApproval", []))
|
||||
approved = set(manifest.get("approvedBaseline", []))
|
||||
next_after = set(manifest.get("nextAfterApproval", []))
|
||||
|
||||
for batch in BATCH_ORDER:
|
||||
batch_rows = [row for row in rows if row.get("batch") == batch]
|
||||
batch_rows = sorted(batch_rows, key=lambda row: row_sort_key(row, manifest))
|
||||
stage_counts = Counter(row.get("review_stage", "queued-rework") for row in batch_rows)
|
||||
units: list[dict[str, Any]] = []
|
||||
for row in batch_rows:
|
||||
stem = row["stem"]
|
||||
queue_role = "approved reference"
|
||||
if stem in pending:
|
||||
queue_role = "current approval sample"
|
||||
elif stem in next_after:
|
||||
queue_role = "next primary queue"
|
||||
elif stem not in approved:
|
||||
queue_role = "queued rework"
|
||||
units.append(
|
||||
{
|
||||
"stem": stem,
|
||||
"reviewStage": row.get("review_stage"),
|
||||
"queueRole": queue_role,
|
||||
"baseStatus": row["base"]["status"],
|
||||
"actionStatus": row["actions"]["status"],
|
||||
"battleBasePx": [
|
||||
row["base"].get("battle_min_width_px"),
|
||||
row["base"].get("battle_max_width_px"),
|
||||
row["base"].get("battle_min_height_px"),
|
||||
row["base"].get("battle_max_height_px"),
|
||||
],
|
||||
"spec": SPEC_BY_STEM.get(stem),
|
||||
}
|
||||
)
|
||||
batches.append(
|
||||
{
|
||||
"key": batch,
|
||||
"label": BATCH_LABELS.get(batch, batch),
|
||||
"total": len(batch_rows),
|
||||
"stageCounts": dict(sorted(stage_counts.items())),
|
||||
"units": units,
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"schema": 1,
|
||||
"manifest": MANIFEST_PATH.relative_to(ROOT).as_posix(),
|
||||
"audit": AUDIT_PATH.relative_to(ROOT).as_posix(),
|
||||
"approvedBaseline": manifest.get("approvedBaseline", []),
|
||||
"pendingApproval": manifest.get("pendingApproval", []),
|
||||
"nextAfterApproval": manifest.get("nextAfterApproval", []),
|
||||
"stageCounts": audit.get("stage_counts", {}),
|
||||
"batches": batches,
|
||||
}
|
||||
|
||||
|
||||
def write_markdown(data: dict[str, Any]) -> None:
|
||||
lines = [
|
||||
"# Unit Sprite Batch Queue",
|
||||
"",
|
||||
"This file is generated by `scripts/write-unit-sprite-batch-queue.py`.",
|
||||
"",
|
||||
"Use this as the long-running queue for the full hand-painted sprite rework. It is inventory and sequencing, not approval by itself.",
|
||||
"",
|
||||
"## Current State",
|
||||
"",
|
||||
f"- Approved baseline units: {len(data['approvedBaseline'])}",
|
||||
f"- Pending approval units: {len(data['pendingApproval'])}",
|
||||
f"- Queued rework units: {data.get('stageCounts', {}).get('queued-rework', 0)}",
|
||||
f"- Manifest: `{data['manifest']}`",
|
||||
f"- Audit: `{data['audit']}`",
|
||||
"",
|
||||
]
|
||||
|
||||
pending = data["pendingApproval"]
|
||||
if pending:
|
||||
lines.extend(
|
||||
[
|
||||
"## Approval Blocker",
|
||||
"",
|
||||
"Do not start the next unit, commit, push, or deploy while these units are pending:",
|
||||
"",
|
||||
]
|
||||
)
|
||||
for stem in pending:
|
||||
lines.append(f"- `{stem}`")
|
||||
lines.append("")
|
||||
|
||||
lines.extend(["## Next After Approval", ""])
|
||||
next_after = data["nextAfterApproval"]
|
||||
if next_after:
|
||||
for index, stem in enumerate(next_after, start=1):
|
||||
spec = SPEC_BY_STEM.get(stem)
|
||||
spec_note = f" - spec `{spec}`" if spec else ""
|
||||
lines.append(f"{index}. `{stem}`{spec_note}")
|
||||
else:
|
||||
lines.append("No next unit is currently defined.")
|
||||
lines.append("")
|
||||
|
||||
lines.extend(["## Batch Queue", ""])
|
||||
for batch in data["batches"]:
|
||||
lines.extend(
|
||||
[
|
||||
f"### {batch['label']}",
|
||||
"",
|
||||
f"- Batch key: `{batch['key']}`",
|
||||
f"- Total units: {batch['total']}",
|
||||
f"- Stage counts: {', '.join(f'{key} {value}' for key, value in batch['stageCounts'].items()) or 'none'}",
|
||||
"",
|
||||
"| unit | role | stage | status | battle base px | spec |",
|
||||
"| --- | --- | --- | --- | --- | --- |",
|
||||
]
|
||||
)
|
||||
for unit in batch["units"]:
|
||||
battle = unit["battleBasePx"]
|
||||
battle_text = f"{battle[0]}..{battle[1]} x {battle[2]}..{battle[3]}"
|
||||
spec = f"`{unit['spec']}`" if unit["spec"] else ""
|
||||
status = (
|
||||
"technical ok"
|
||||
if unit["baseStatus"] == "ok" and unit["actionStatus"] == "ok"
|
||||
else f"base {unit['baseStatus']}; actions {unit['actionStatus']}"
|
||||
)
|
||||
lines.append(
|
||||
f"| `{unit['stem']}` | {unit['queueRole']} | {unit['reviewStage']} | {status} | {battle_text} | {spec} |"
|
||||
)
|
||||
lines.append("")
|
||||
|
||||
MARKDOWN_PATH.write_text("\n".join(lines), encoding="utf-8")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
manifest = load_json(MANIFEST_PATH)
|
||||
audit = load_json(AUDIT_PATH)
|
||||
data = queue_data(manifest, audit)
|
||||
JSON_PATH.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
write_markdown(data)
|
||||
print(f"Wrote {JSON_PATH}")
|
||||
print(f"Wrote {MARKDOWN_PATH}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user