fix(rules): undead and constructs are destroyed at hp <= 0
SRD Undead/Construct traits: 'immediately destroyed when reduced to 0 hit points or fewer' — undead and constructs have no Constitution score, so the PF1e hp <= -CON death rule does not apply to them. - combat.py: CombatantState.death_threshold returns 0 for creature_type undead/construct (in addition to the opt-in dies_at_zero flag) - tests: TestUndeadDeathThreshold pins undead dead at 0 and below, living keeps the disabled band at 0 and the dying band down to -CON - golden unchanged: tammara-vs-lich has the lich winning in 1 round without taking damage
This commit is contained in:
@@ -769,9 +769,13 @@ class CombatantState:
|
|||||||
@property
|
@property
|
||||||
def death_threshold(self) -> int:
|
def death_threshold(self) -> int:
|
||||||
"""PF1e (CRB): death when negative hp reaches the CON score (no -10 rule).
|
"""PF1e (CRB): death when negative hp reaches the CON score (no -10 rule).
|
||||||
The optional simple monster-death rule raises the threshold to 0."""
|
Undead and constructs have no Constitution score and are "immediately
|
||||||
|
destroyed when reduced to 0 hit points or fewer" (SRD Undead/Construct
|
||||||
|
traits). The optional simple monster-death rule raises the threshold to 0."""
|
||||||
if self.dies_at_zero:
|
if self.dies_at_zero:
|
||||||
return 0
|
return 0
|
||||||
|
if self.combatant.creature_type in ("undead", "construct"):
|
||||||
|
return 0
|
||||||
return -self.combatant.abilities.con_score
|
return -self.combatant.abilities.con_score
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ def _make_lich() -> Combatant:
|
|||||||
name="Lich",
|
name="Lich",
|
||||||
level=11,
|
level=11,
|
||||||
size="Medium",
|
size="Medium",
|
||||||
|
creature_type="undead",
|
||||||
abilities=AbilityScores(
|
abilities=AbilityScores(
|
||||||
str_score=12, dex_score=14, con_score=14,
|
str_score=12, dex_score=14, con_score=14,
|
||||||
int_score=20, wis_score=14, cha_score=16,
|
int_score=20, wis_score=14, cha_score=16,
|
||||||
@@ -173,6 +174,42 @@ class TestFearAura:
|
|||||||
assert all(ac.condition.name != "shaken" for ac in foe_state.active_conditions)
|
assert all(ac.condition.name != "shaken" for ac in foe_state.active_conditions)
|
||||||
|
|
||||||
|
|
||||||
|
# ── Undead death threshold ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
class TestUndeadDeathThreshold:
|
||||||
|
"""Given: an undead creature (no CON score, SRD Undead traits)
|
||||||
|
When: its hp reaches 0 or below
|
||||||
|
Then: it is immediately destroyed (dead), unlike a living creature
|
||||||
|
which keeps the disabled band at 0 and the dying band down to -CON."""
|
||||||
|
|
||||||
|
def test_undead_dead_at_zero(self) -> None:
|
||||||
|
lich = _make_lich()
|
||||||
|
state = _make_state(lich, "monsters", (1, 1))
|
||||||
|
state.hp = 0
|
||||||
|
assert state.dead is True
|
||||||
|
assert state.active is False
|
||||||
|
|
||||||
|
def test_undead_dead_below_zero(self) -> None:
|
||||||
|
lich = _make_lich()
|
||||||
|
state = _make_state(lich, "monsters", (1, 1))
|
||||||
|
state.hp = -1
|
||||||
|
assert state.dead is True
|
||||||
|
|
||||||
|
def test_living_not_dead_at_zero(self) -> None:
|
||||||
|
foe = _make_foe()
|
||||||
|
state = _make_state(foe, "players", (1, 1))
|
||||||
|
state.hp = 0
|
||||||
|
assert state.dead is False
|
||||||
|
assert state.active is True
|
||||||
|
|
||||||
|
def test_living_not_dead_in_dying_band(self) -> None:
|
||||||
|
foe = _make_foe()
|
||||||
|
state = _make_state(foe, "players", (1, 1))
|
||||||
|
state.hp = -1 # -1 > -CON (12)
|
||||||
|
assert state.dead is False
|
||||||
|
|
||||||
|
|
||||||
# ── JSON integrity ────────────────────────────────────────────────────────────
|
# ── JSON integrity ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user