fix(rules): Exalted of the Society grants +1 channel energy/day via resource_grants; Fortified Drinker becomes documented no-op (SRD: +2 saves vs mind-affecting after drinking, alcohol state not modeled)
This commit is contained in:
@@ -195,6 +195,31 @@ def _resolve_features(raw_features: list[Json], level: int) -> list[AbilitySpec]
|
||||
]
|
||||
|
||||
|
||||
def _merge_resource_grants(
|
||||
raw_resources: Json, specs: list[AbilitySpec]
|
||||
) -> dict[str, int]:
|
||||
"""Merge the JSON resource pool with per-day grants from resolved features.
|
||||
|
||||
Traits like Exalted of the Society ("You may channel energy one additional
|
||||
time per day") store their benefit in ``AbilitySpec.resource_grants``
|
||||
because the engine keeps resource pools (``Combatant.resources`` ->
|
||||
``CombatantState.resources_left``), not stat modifiers, for per-day uses.
|
||||
"""
|
||||
merged: dict[str, int] = {}
|
||||
if isinstance(raw_resources, dict):
|
||||
merged.update(
|
||||
{
|
||||
key: value
|
||||
for key, value in raw_resources.items()
|
||||
if isinstance(value, int) and not isinstance(value, bool)
|
||||
}
|
||||
)
|
||||
for spec in specs:
|
||||
for resource_name, amount in spec.resource_grants:
|
||||
merged[resource_name] = merged.get(resource_name, 0) + amount
|
||||
return merged
|
||||
|
||||
|
||||
def _auto_attack_ids(data: dict[str, Json], monster_id: str) -> None:
|
||||
"""Auto-assign attack ids from file stem if missing."""
|
||||
attacks = data.get("attacks")
|
||||
@@ -240,7 +265,11 @@ def load_combatant(path: Path) -> Combatant:
|
||||
raw_features = data.pop("features", None)
|
||||
validate_payload: dict[str, object] = dict(data)
|
||||
if isinstance(raw_features, list):
|
||||
validate_payload["features"] = _resolve_features(raw_features, level)
|
||||
specs = _resolve_features(raw_features, level)
|
||||
validate_payload["features"] = specs
|
||||
granted = _merge_resource_grants(data.get("resources"), specs)
|
||||
if granted:
|
||||
validate_payload["resources"] = granted
|
||||
|
||||
# spells stays as list[str] — Pydantic validates it directly
|
||||
|
||||
|
||||
Reference in New Issue
Block a user