feat(rules): add alignment field to Combatant with axis gating flags

Adds the PF1e two-axis alignment code (LG/NG/CG/LN/N/CN/LE/NE/CE,
default N) to the Combatant model with a strict validator, derived
is_lawful/is_chaotic/is_good/is_evil properties, exposure in the
observation snapshot, and alignment values for all monsters and PCs
sourced from the bestiary KB and the Foundry sheets.
This commit is contained in:
2026-09-20 11:49:04 +02:00
parent 40088e1869
commit f9e51e377a
20 changed files with 265 additions and 2 deletions
+43
View File
@@ -281,6 +281,15 @@ class DamageReduction(_FrozenModel):
bypass: frozenset[str] = frozenset()
_ALIGNMENT_CODES: frozenset[str] = frozenset(
{"LG", "NG", "CG", "LN", "N", "CN", "LE", "NE", "CE"}
)
_LAWFUL_CODES: frozenset[str] = frozenset({"LG", "LN", "LE"})
_CHAOTIC_CODES: frozenset[str] = frozenset({"CG", "CN", "CE"})
_GOOD_CODES: frozenset[str] = frozenset({"LG", "NG", "CG"})
_EVIL_CODES: frozenset[str] = frozenset({"LE", "NE", "CE"})
class Combatant(_FrozenModel):
"""A fully parsed combatant, ready for the engine."""
@@ -320,3 +329,37 @@ class Combatant(_FrozenModel):
# begin." Unaware combatants do not act in the surprise round. Default
# True keeps every existing encounter on a full first round.
aware: bool = True
# PF1e two-axis alignment (SRD "Alignment"): law-good axis codes, one of
# LG/NG/CG/LN/N/CN/LE/NE/CE. Default "N" (true neutral).
alignment: str = "N"
@field_validator("alignment")
@classmethod
def _validate_alignment(cls, value: str) -> str:
if value not in _ALIGNMENT_CODES:
msg = (
f"alignment must be one of {', '.join(sorted(_ALIGNMENT_CODES))}, "
f"got {value!r}"
)
raise ValueError(msg)
return value
@property
def is_lawful(self) -> bool:
"""Law axis: LG/LN/LE."""
return self.alignment in _LAWFUL_CODES
@property
def is_chaotic(self) -> bool:
"""Law axis: CG/CN/CE."""
return self.alignment in _CHAOTIC_CODES
@property
def is_good(self) -> bool:
"""Good axis: LG/NG/CG."""
return self.alignment in _GOOD_CODES
@property
def is_evil(self) -> bool:
"""Evil axis: LE/NE/CE."""
return self.alignment in _EVIL_CODES