Files
code_of_conquest_dnd/tools/content_build/model.py

77 lines
2.4 KiB
Python

"""Typed entries + the envelope/category rules. Pure data; no I/O. The npc
interactive layer vs the person world-record is distinguished by id NAMESPACE
(prefix), not by `type` (both are type 'person')."""
from dataclasses import dataclass, field
from .errors import BuildError
from .parse import RawEntry
CANON_TYPES = {"town", "place", "region", "faction", "person", "rule"}
KNOWLEDGE_TYPES = {"rumor", "fact", "secret"}
LEGAL_NAMESPACES = {"town", "place", "region", "faction", "person", "rule",
"rumor", "fact", "secret", "npc"}
STATUSES = {"candidate", "canon"}
@dataclass
class KnowsLink:
fact_id: str
gate: str
@dataclass
class Entry:
id: str
type: str
status: str
secrecy: int
related: list
body: str | None
source: str
start_disposition: str | None = None
knows: list = field(default_factory=list) # list[KnowsLink]
disposition_notes: str | None = None
rungs: list | None = None
@property
def namespace(self) -> str:
return self.id.split(".", 1)[0]
@property
def slug(self) -> str:
parts = self.id.split(".", 1)
return parts[1] if len(parts) == 2 else parts[0]
@property
def is_npc_layer(self) -> bool:
return self.namespace == "npc"
@property
def is_knowledge(self) -> bool:
return self.type in KNOWLEDGE_TYPES
@property
def is_canon_entity(self) -> bool:
return (not self.is_npc_layer) and self.type in CANON_TYPES
def entry_from_raw(raw: RawEntry) -> Entry:
d = raw.data
for key in ("id", "type", "status", "secrecy", "related"):
if key not in d:
raise BuildError(f"missing required field '{key}'",
source=raw.source, entry_id=d.get("id", ""))
knows: list[KnowsLink] = []
for k in d.get("knows", []):
if "fact" not in k or "gate" not in k:
raise BuildError("knows entry needs 'fact' and 'gate'",
source=raw.source, entry_id=d.get("id", ""))
knows.append(KnowsLink(fact_id=k["fact"], gate=k["gate"]))
return Entry(
id=d["id"], type=d["type"], status=d["status"], secrecy=d["secrecy"],
related=list(d["related"]), body=d.get("body"), source=raw.source,
start_disposition=d.get("start_disposition"), knows=knows,
disposition_notes=d.get("disposition_notes"), rungs=d.get("rungs"),
)