fix(rules): exclude weapon special ability dice from critical multiplication

PF1e (CRB Critical Hits): "additional damage dice from weapon special
abilities (such as flaming) are not multiplied when you score a critical
hit". _resolve_hit_damage now multiplies only the physical dice plus flat
bonuses by crit_mult; energy-typed component dice are added at 1x (the
energy share on the DR path and the per-component resistance step drop
their crit factor). Sneak precision dice were already excluded. README
and the module convention list updated.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-09-12 11:21:35 +02:00
parent a9b33e4055
commit a4f7f4e5de
3 changed files with 46 additions and 14 deletions
+17
View File
@@ -125,6 +125,23 @@ def test_fire_immunity_negates_only_fire_component() -> None:
assert eng._states[1].hp == 94
def test_crit_does_not_multiply_flaming_dice() -> None:
"""CRB Critical Hits: "additional damage dice from weapon special
abilities (such as flaming) are not multiplied when you score a critical
hit" — the slashing die rides the x2, the fire die stays at 1x."""
weapon = _attack("fiery-blade", _MIXED)
hero = _combatant("hero", attacks=(weapon,))
foe = _combatant("foe")
# d20=20 threatens, confirm 20 auto-crits, slashing 1d6=6, fire 1d6=4.
eng = _engine([hero, foe], [(0, 0), (0, 1)], ["players", "monsters"],
[20, 20, 6, 4])
result = eng.resolve_attack(eng._states[0], eng._states[1], weapon)
assert result.crit is True
# 6x2 + 4 = 16 — NOT (6+4)x2 = 20 (the old multiply-everything convention).
assert result.damage == 16
assert eng._states[1].hp == 84
def test_physical_only_weapon_ignores_energy_resistance() -> None:
"""(c) a physical-only weapon vs fire resistance is untouched."""
weapon = _attack("sword", (("1d6", ("slashing",)),))