fix(rules): Weapon Focus filter matches weapon names case-insensitively (substring)

The filter 'Pistol' now matches 'Braises d'Automne (pistolet ?)' via
case-insensitive substring matching, so the feat is no longer dead.
Golden test_tammara_vs_goblin updated: round-4 pistol shot gains the +1
feat bonus (total 15 -> 16 vs AC 16), turning the MISS into a lethal HIT
(17 damage, goblin 6 -> -11); the goblin dies on round 4 so the old
rounds 4-10 exchange and the draw are gone.
This commit is contained in:
2026-09-16 19:45:56 +02:00
parent f31cc88124
commit 4d45409941
3 changed files with 64 additions and 20 deletions
+17 -1
View File
@@ -307,6 +307,19 @@ def _size_rank(size: str) -> int:
except ValueError:
return _SIZE_ORDER.index("medium")
def _weapon_filter_matches(weapon_filter: str, weapon_name: str | None) -> bool:
"""True when a feat's weapon_filter names the weapon being used.
PF1e Weapon Focus is taken by weapon GROUP ("Pistol"), while sheets name
individual weapons ("Braises d'Automne (pistolet ?)"), so the match is a
case-insensitive substring test of the filter against the weapon name.
A None weapon name (no weapon context) never matches a filtered modifier.
"""
if weapon_name is None:
return False
return weapon_filter.lower() in weapon_name.lower()
# PF1e natural reach for tall creatures (simplification: no long/quadruped
# distinction). Tiny, Diminutive and Fine have a natural reach of 0 ft (SRD
# "Big and Little Creatures in Combat"): "Creatures that take up less than 1
@@ -2568,7 +2581,10 @@ class CombatEngine:
m
for m in feat.effects
if m.target == target
and (m.weapon_filter is None or m.weapon_filter == weapon_name)
and (
m.weapon_filter is None
or _weapon_filter_matches(m.weapon_filter, weapon_name)
)
)
# PF1e: prone and helpless (paralyzed/asleep/unconscious) AC
# adjustments depend on the weapon kind and are applied in