87 lines
2.7 KiB
Python
87 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Verify the Phase 7.1 single-item and individual-level design contract."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
AUTHORITY_PATHS = [
|
|
"docs/product/GAME_PRODUCT_CONTRACT.md",
|
|
"docs/product/SYSTEMS_AND_BATTLE.md",
|
|
"docs/product/ECONOMY_AND_BALANCE.md",
|
|
"docs/product/SAVE_AND_FAILURE_CONTRACT.md",
|
|
"docs/content/RECRUIT_GENERATION.md",
|
|
"docs/content/TRAIT_CATALOG.md",
|
|
"docs/content/EQUIPMENT_CATALOG.md",
|
|
"docs/presentation/SCREEN_AND_STATE_MAP.md",
|
|
"docs/presentation/UI_AND_VISUAL_SYSTEM.md",
|
|
"docs/presentation/ART_ASSET_AND_ANIMATION_CATALOG.md",
|
|
]
|
|
|
|
FORBIDDEN = [
|
|
r"three equipment slots",
|
|
r"all three equipment slots",
|
|
r"starting_equipment_ids\[\]",
|
|
r"one or two starter items",
|
|
r"two starter items",
|
|
r"two-item starts",
|
|
r"Fallen Recruit owns three items",
|
|
r"all three item IDs returned",
|
|
r"another equipped item",
|
|
r"three different equipped trigger tags",
|
|
]
|
|
|
|
|
|
def main() -> int:
|
|
errors: list[str] = []
|
|
corpus = "\n".join(
|
|
(ROOT / path).read_text(encoding="utf-8") for path in AUTHORITY_PATHS
|
|
)
|
|
lowered = corpus.lower()
|
|
|
|
for pattern in FORBIDDEN:
|
|
if re.search(pattern, lowered):
|
|
errors.append(f"forbidden multi-item contract remains: {pattern}")
|
|
|
|
required = {
|
|
"one universal active equipment slot": "systems one-slot authority",
|
|
"starting_equipment_id, level, xp": "Recruit schema",
|
|
"level cap 10": "individual XP authority",
|
|
"one-slot replacement receipt": "save transaction",
|
|
"角色独立等级": "Chinese UI level label",
|
|
"唯一装备槽": "Chinese UI equipment label",
|
|
"XP 44/50": "visible current XP progress",
|
|
"EQ-014 退回本轮库存": "visible replacement return",
|
|
}
|
|
ui_text = (ROOT / "prototype/site/app.js").read_text(encoding="utf-8")
|
|
combined = f"{corpus}\n{ui_text}"
|
|
for phrase, label in required.items():
|
|
if phrase not in combined:
|
|
errors.append(f"missing {label}: {phrase}")
|
|
|
|
equipment = (ROOT / "docs/content/EQUIPMENT_CATALOG.md").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
ids = re.findall(r"^\| `EQ-(\d{3})`", equipment, flags=re.MULTILINE)
|
|
if len(ids) != 320 or len(set(ids)) != 320:
|
|
errors.append(f"equipment identity count changed: rows={len(ids)} unique={len(set(ids))}")
|
|
|
|
if errors:
|
|
for error in errors:
|
|
print(f"ERROR: {error}")
|
|
return 1
|
|
|
|
print(
|
|
"ABG_RECRUIT_LOADOUT_LEVEL_OK "
|
|
"equipment_slots=1 level_scope=individual level_cap=10 equipment=320"
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|