feat(data): wire all 55 PC feats
This commit is contained in:
@@ -8,13 +8,14 @@ as-is.
|
||||
|
||||
Feat names are matched case-insensitively. Known feats (Power Attack,
|
||||
Iron Will, Weapon Focus, Toughness, etc.) map to their ``AbilitySpec``
|
||||
constants. Unknown feat names produce a minimal ``AbilitySpec`` with no
|
||||
mechanical effects — the data is preserved for future implementation.
|
||||
constants. Unmatched feat names produce a traced no-op (debug log +
|
||||
minimal ``AbilitySpec``) so no name silently disappears.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
@@ -26,20 +27,66 @@ if TYPE_CHECKING:
|
||||
from pf1e_simulator.loaders.foundry import Json
|
||||
|
||||
from pf1e_simulator.abilities import (
|
||||
ADOPTED,
|
||||
ALERTNESS,
|
||||
AMBITIOUS,
|
||||
ARMOR_PROFICIENCY_LIGHT,
|
||||
ARMOR_PROFICIENCY_MEDIUM,
|
||||
AXE_TO_GRIND,
|
||||
BITTER,
|
||||
BORN_DAMNED,
|
||||
BREEZE_KISSED,
|
||||
BREW_POTION,
|
||||
COMBAT_REFLEXES,
|
||||
CRAFT_WONDROUS_ITEM,
|
||||
DAZZLING_DISPLAY,
|
||||
DODGE,
|
||||
ESCHEW_MATERIALS,
|
||||
EXALTED_SOCIETY,
|
||||
EXTRA_HEX,
|
||||
EXTREMELY_FASHIONABLE,
|
||||
FATES_FAVORED,
|
||||
FEY_TOUCHED,
|
||||
FOCUSED_STUDY,
|
||||
FORTIFIED_DRINKER,
|
||||
FRIENDLY_FACE,
|
||||
GIFTED_ADEPT,
|
||||
GREAT_FORTITUDE,
|
||||
GUNSMITHING,
|
||||
HALF_FORGOTTEN_SECRETS,
|
||||
HARMONIC_SPELL,
|
||||
IRON_WILL,
|
||||
LARGER_THAN_LIFE,
|
||||
LIGHTNING_REFLEXES,
|
||||
LIKE_THE_WIND,
|
||||
LONELY,
|
||||
MAW_OR_CLAW,
|
||||
MOUNTED_COMBAT,
|
||||
PINGRE,
|
||||
POINT_BLANK_SHOT,
|
||||
POWER_ATTACK,
|
||||
PRECISE_SHOT,
|
||||
PREHENSILE_TAIL,
|
||||
RAPID_RELOAD,
|
||||
SACRED_SUMS,
|
||||
SCALED_SKIN_ELECTRICITY,
|
||||
SELECTIVE_CHANNELING,
|
||||
SKILL_FOCUS_PERCEPTION,
|
||||
SPELL_VULNERABILITY_DIVINATION,
|
||||
STEALTH_SYNERGY,
|
||||
SUMMON_GOOD_MONSTER,
|
||||
THOROUGHBRED,
|
||||
VAGABOND_CHILD,
|
||||
WEAPON_FINESSE,
|
||||
WHISPERING_WIND,
|
||||
AbilitySpec,
|
||||
toughness,
|
||||
weapon_focus,
|
||||
)
|
||||
from pf1e_simulator.models import Combatant
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# ── Feat lookup ────────────────────────────────────────────────────────────────
|
||||
|
||||
_WEAPON_FOCUS_RE = re.compile(r"^Weapon Focus \((.+)\)$", re.IGNORECASE)
|
||||
@@ -53,16 +100,64 @@ _KNOWN_FEATS: dict[str, AbilitySpec] = {
|
||||
"point-blank shot": POINT_BLANK_SHOT,
|
||||
"precise shot": PRECISE_SHOT,
|
||||
"dodge": DODGE,
|
||||
"mounted combat": MOUNTED_COMBAT,
|
||||
"stealth synergy": STEALTH_SYNERGY,
|
||||
"adopted": ADOPTED,
|
||||
"thoroughbred": THOROUGHBRED,
|
||||
"thoroughbred (human; keleshite)": THOROUGHBRED,
|
||||
"vagabond child (urban)": VAGABOND_CHILD,
|
||||
"whispering wind": WHISPERING_WIND,
|
||||
"breeze-kissed": BREEZE_KISSED,
|
||||
"like the wind": LIKE_THE_WIND,
|
||||
"friendly face": FRIENDLY_FACE,
|
||||
"fey-touched": FEY_TOUCHED,
|
||||
"combat reflexes": COMBAT_REFLEXES,
|
||||
"armor proficiency (light)": ARMOR_PROFICIENCY_LIGHT,
|
||||
"armor proficiency (medium)": ARMOR_PROFICIENCY_MEDIUM,
|
||||
"harmonic spell": HARMONIC_SPELL,
|
||||
"weapon finesse": WEAPON_FINESSE,
|
||||
"axe to grind (combat)": AXE_TO_GRIND,
|
||||
"spell vulnerability (divination; drawback)": SPELL_VULNERABILITY_DIVINATION,
|
||||
"born damned (race: tiefling)": BORN_DAMNED,
|
||||
"gifted adept (dispel magic; magic)": GIFTED_ADEPT,
|
||||
"prehensile tail (tiefling daemon-spawn)": PREHENSILE_TAIL,
|
||||
"scaled skin (electricity) (tiefling daemon-spawn)": SCALED_SKIN_ELECTRICITY,
|
||||
"maw or claw (bite) (tiefling daemon-spawn)": MAW_OR_CLAW,
|
||||
"brew potion": BREW_POTION,
|
||||
"craft wondrous item": CRAFT_WONDROUS_ITEM,
|
||||
"extra hex": EXTRA_HEX,
|
||||
"eschew materials": ESCHEW_MATERIALS,
|
||||
"half-forgotten secrets (dhampir; ajibachana)": HALF_FORGOTTEN_SECRETS,
|
||||
"pingre": PINGRE,
|
||||
"summon good monster": SUMMON_GOOD_MONSTER,
|
||||
"sacred summons": SACRED_SUMS,
|
||||
"selective channeling": SELECTIVE_CHANNELING,
|
||||
"extremely fashionable": EXTREMELY_FASHIONABLE,
|
||||
"exalted of the society (cleric, pathfinder society)": EXALTED_SOCIETY,
|
||||
"bitter": BITTER,
|
||||
"ambitious": AMBITIOUS,
|
||||
"rapid reload (pistol)": RAPID_RELOAD,
|
||||
"gunsmithing": GUNSMITHING,
|
||||
"skill focus (perception)": SKILL_FOCUS_PERCEPTION,
|
||||
"dazzling display": DAZZLING_DISPLAY,
|
||||
"fortified drinker (cayden cailean)": FORTIFIED_DRINKER,
|
||||
"fate's favored": FATES_FAVORED,
|
||||
"lonely": LONELY,
|
||||
"larger than life": LARGER_THAN_LIFE,
|
||||
"focused study (trait racial humain \u2014 remplace le bonus feat)": FOCUSED_STUDY,
|
||||
}
|
||||
|
||||
# Feat names with no combat-mechanical definition (flavor / race descriptors / jokes).
|
||||
# These are resolved to traced no-ops so the loader never silently drops a name.
|
||||
_NOOP_FEATS: frozenset[str] = frozenset({
|
||||
"6. vert - yes mistress",
|
||||
"animal (race)",
|
||||
"drake (race)",
|
||||
"orange - lazy ass",
|
||||
})
|
||||
|
||||
|
||||
def _resolve_feat(name: str, level: int) -> AbilitySpec:
|
||||
"""Resolve a feat name string to an AbilitySpec.
|
||||
|
||||
Known feats → their constant. Toughness → factory with level.
|
||||
Weapon Focus (X) → factory with weapon name. Unknown feats → minimal
|
||||
AbilitySpec with no effects (data preserved for future implementation).
|
||||
"""
|
||||
stripped = name.strip()
|
||||
lower = stripped.lower()
|
||||
if lower in _KNOWN_FEATS:
|
||||
@@ -72,6 +167,10 @@ def _resolve_feat(name: str, level: int) -> AbilitySpec:
|
||||
wf_match = _WEAPON_FOCUS_RE.match(stripped)
|
||||
if wf_match:
|
||||
return weapon_focus(wf_match.group(1))
|
||||
if lower in _NOOP_FEATS:
|
||||
logger.debug("feat traced-noop: %r", stripped)
|
||||
return AbilitySpec(name=stripped, category="feat")
|
||||
logger.debug("feat unmatched (no definition): %r", stripped)
|
||||
return AbilitySpec(name=stripped, category="feat")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user