feat(rules): nonlethal sources - unarmed strikes deal nonlethal by default (SRD Unarmed Attacks), Minimum Damage floor lands 1 nonlethal (CRB Damage), transcript + AttackResult expose the track

This commit is contained in:
2026-09-20 18:54:22 +02:00
parent cba7bbf3c9
commit 83c6e541ac
4 changed files with 257 additions and 38 deletions
+7 -5
View File
@@ -251,14 +251,16 @@ def test_dr_spares_the_energy_component_of_a_mixed_attack() -> None:
def test_physical_only_damage_vs_dr_unchanged() -> None:
"""(h) a physical-only weapon keeps the exact old DR arithmetic (regression
lock for the split): 1d8 = 3 vs DR 5 reduces below 1 — the CRB "Damage"
Minimum Damage floor then leaves a landed hit dealing 1 (nonlethal is not
modeled: 1 point of normal damage)."""
lock for the split): 1d8 = 3 vs DR 5 reduces below 1 - the CRB "Damage"
Minimum Damage floor then leaves a landed hit dealing 1 point of
NONLETHAL damage (SRD "Nonlethal Damage": the floor never touches hp)."""
weapon = _attack("warhammer", (("1d8", ("bludgeoning",)),))
hero = _combatant("hero", attacks=(weapon,))
foe = _combatant("foe").model_copy(update={"dr": DamageReduction(amount=5)})
eng = _engine([hero, foe], [(0, 0), (0, 1)], ["players", "monsters"],
[15, 3])
result = eng.resolve_attack(eng._states[0], eng._states[1], weapon)
assert result.damage == 1
assert eng._states[1].hp == 99 # the Minimum Damage floor lands 1 point
assert result.damage == 0
assert result.nonlethal == 1
assert eng._states[1].hp == 100 # the Minimum Damage floor is nonlethal
assert eng._states[1].nonlethal == 1