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:
@@ -16,11 +16,16 @@ from pf1e_simulator.abilities import (
|
||||
MOUNTED_COMBAT,
|
||||
POINT_BLANK_SHOT,
|
||||
)
|
||||
from pf1e_simulator.combat import CombatantState, CombatEngine
|
||||
from pf1e_simulator.grid import Grid
|
||||
from pf1e_simulator.loaders.combatant import (
|
||||
_KNOWN_FEATS,
|
||||
_NOOP_FEATS,
|
||||
_resolve_feat,
|
||||
load_combatant,
|
||||
)
|
||||
from pf1e_simulator.map import MapSpec, TerrainType
|
||||
from pf1e_simulator.rng import ScriptedRng
|
||||
|
||||
PC_FILES = [
|
||||
"data/pcs/esha.json",
|
||||
@@ -100,6 +105,30 @@ def test_spot_check_weapon_focus_pistol() -> None:
|
||||
assert any("Pistol" in str(e.weapon_filter) for e in spec.effects)
|
||||
|
||||
|
||||
def test_weapon_focus_applies_to_real_tammara_pistols() -> None:
|
||||
# The feat filter "Pistol" must match tammara's real weapon names
|
||||
# ("Braises d'Automne (pistolet ?)", "Adieux du Printemps
|
||||
# (pistolet, masterwork)") via case-insensitive substring matching,
|
||||
# and must NOT match her rapier or alchemist's fire.
|
||||
tammara = load_combatant(Path("data/pcs/tammara.json"))
|
||||
state = CombatantState(
|
||||
combatant=tammara, side="players", pos=(0, 0), hp=tammara.hp_max
|
||||
)
|
||||
legend = {".": TerrainType(type="floor", move_cost=1)}
|
||||
grid = Grid.from_spec(MapSpec(name="t", terrain=tuple([".." for _ in range(2)]), legend=legend))
|
||||
engine = CombatEngine(ScriptedRng([]), grid, [state])
|
||||
|
||||
def attack_total(weapon_name: str | None) -> int:
|
||||
mods = engine._stat_modifiers(state, "attack", weapon_name=weapon_name)
|
||||
return sum(m.value for m in mods if m.weapon_filter is not None)
|
||||
|
||||
assert attack_total("Braises d'Automne (pistolet ?)") == 1
|
||||
assert attack_total("Adieux du Printemps (pistolet, masterwork)") == 1
|
||||
assert attack_total("Rapier (masterwork)") == 0
|
||||
assert attack_total("Alchemist's Fire") == 0
|
||||
assert attack_total(None) == 0
|
||||
|
||||
|
||||
def test_spot_check_toughness_level_tied() -> None:
|
||||
spec = _resolve_feat("Toughness", 5)
|
||||
assert spec.name == "Toughness"
|
||||
|
||||
Reference in New Issue
Block a user