60 lines
2.7 KiB
Python
60 lines
2.7 KiB
Python
"""Shared content identities and item presentation used by runtime and assets."""
|
|
import json
|
|
from pathlib import Path
|
|
|
|
PATH = Path(__file__).resolve().parents[2] / "catalog" / "content.json"
|
|
|
|
|
|
def load():
|
|
data = json.loads(PATH.read_text(encoding="utf-8"))
|
|
items = data["items"]
|
|
ids = [item["id"] for item in items]
|
|
if len(ids) != len(set(ids)):
|
|
raise ValueError("duplicate item ID in content catalog")
|
|
referenced = []
|
|
for group in (data["aircraft"], data["units"]):
|
|
kinds = [entry["kind"] for entry in group]
|
|
if len(kinds) != len(set(kinds)):
|
|
raise ValueError("duplicate kind in content catalog")
|
|
referenced.extend(entry["item"] for entry in group if entry["item"] is not None)
|
|
if len(referenced) != len(set(referenced)) or not set(referenced) <= set(ids):
|
|
raise ValueError("duplicate or unregistered content item")
|
|
for item in items:
|
|
if len(item["color"]) != 3 or any(not isinstance(c, int) or not 0 <= c <= 255 for c in item["color"]):
|
|
raise ValueError(f"invalid icon color for {item['id']}")
|
|
return data
|
|
|
|
|
|
def load_sounds():
|
|
"""Load stable event IDs and engine attenuation metadata."""
|
|
import re
|
|
|
|
entries = json.loads((PATH.parent / "sounds.json").read_text(encoding="utf-8"))
|
|
seen = set()
|
|
for entry in entries:
|
|
event, distance, far = entry["event"], entry["distance"], entry["far"]
|
|
if not re.fullmatch(r"[a-z0-9_]+\.[a-z0-9_]+", event) or event in seen:
|
|
raise ValueError(f"invalid or duplicate sound event {event!r}")
|
|
if type(distance) is not int or distance <= 0 or type(far) is not bool:
|
|
raise ValueError(f"invalid sound metadata for {event}")
|
|
seen.add(event)
|
|
names = [entry["event"].replace(".", "_") for entry in entries]
|
|
if len(names) != len(set(names)):
|
|
raise ValueError("sound event IDs collide as resource names")
|
|
registered = [name for entry in entries for name in ([entry["event"], entry["event"] + "_far"] if entry["far"] else [entry["event"]])]
|
|
if len(registered) != len(set(registered)):
|
|
raise ValueError("sound event IDs collide with far variants")
|
|
return entries
|
|
|
|
|
|
def sound_events(entries):
|
|
"""Build the Minecraft sound event registry without generating audio."""
|
|
events = {}
|
|
for entry in entries:
|
|
event, distance = entry["event"], entry["distance"]
|
|
name = event.replace(".", "_")
|
|
events[event] = {"sounds": [{"name": f"airdef:{name}", "attenuation_distance": distance}]}
|
|
if entry["far"]:
|
|
events[event + "_far"] = {"sounds": [{"name": f"airdef:{name}_far", "attenuation_distance": int(distance * 1.6)}]}
|
|
return events
|