fix(rules): tick bleed and auto-fail stabilization for a dying creature

PF1e (CRB, Stable Characters and Recovery): characters taking continuous damage such as bleed automatically fail all Constitution checks made to stabilize, losing 1 hp per round in addition to the continuous damage. _process_dying never applied bleed (only _turn_gate, reached by active creatures did), so a bleeding dying creature lost 0 hp per round. It now ticks bleed_damage at the start of the dying turn and auto-fails the check (no d20 consumed), then evaluates death.

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-11 21:06:10 +02:00
parent 32c09df264
commit 1b5cac234e
3 changed files with 68 additions and 2 deletions
+20 -1
View File
@@ -1816,7 +1816,9 @@ class CombatEngine:
(d20 + Con modifier - negative hp total); a natural 20 auto-succeeds.
Failure bleeds 1 more hp; success stabilizes (no further loss while
stable). New damage below the stabilizing hp resumes dying; healing to
hp >= 0 resets stabilization for any future downing.
hp >= 0 resets stabilization for any future downing. A creature taking
continuous damage (bleed) instead ticks the bleed and AUTOMATICALLY
FAILS the check (CRB, Stable Characters and Recovery).
"""
if state.dead:
return
@@ -1827,6 +1829,23 @@ class CombatEngine:
if state.hp >= state.stabilized_hp:
return
state.stabilized_hp = None
# PF1e (CRB, Stable Characters and Recovery): "Characters taking
# continuous damage, such as from an acid arrow or a bleed effect,
# automatically fail all Constitution checks made to stabilize. Such
# characters lose 1 hit point per round in addition to the continuous
# damage." This is the inactive creature's once-per-round slot (the
# active path already ticked bleed in _turn_gate), so the bleed damage
# is applied here and the d20 check is skipped (auto-fail).
if state.bleed_damage > 0:
self._apply_bleed_damage(state)
hp_before = state.hp
state.hp -= 1
self._log(
f"round {self._current_round} {state.combatant.id}: dying — continuous "
f"damage auto-fails the Constitution check, loses 1 hp "
f"(hp {hp_before} -> {state.hp})"
)
return
con_mod = (state.combatant.abilities.con_score - 10) // 2
penalty = -state.hp
roll = self._rng.d20()