What happens
TrapEffect lets a trap set both kills=True and save.on_save="half". When it does, a character who passes the saving throw is killed anyway.
Why
_resolve_trap (src/osrlib/crawl/exploration.py:780-798) branches on the save result:
if save.passed:
if effect.save.on_save == "negates":
negated = True
else:
halved = True
if negated:
continue
if effect.kills:
events.extend(kill(victim))
continue
Only negates short-circuits the victim. A passing half save sets halved — which is meaningful only for the damage roll further down — and then falls straight through to effect.kills. halved is never consulted again on that path, so the save has no effect at all.
The result is a trap that reads as "save vs. death or die, half on a successful save" and behaves as "everyone the trap catches dies."
Where the fix belongs
TrapEffect already carries a model validator holding two cross-field implications (dungeon.py:262-273: a volley needs damage dice, a duration needs a condition), so this is the same class of rule and the same place for it. Two options:
- Reject the combination. Add
kills=True with save.on_save="half" to _condition_duration_needs_a_condition's sibling rules — half of a kill isn't a thing B/X expresses, so the model can refuse to represent it. This is the option consistent with how the other two implications are handled, and it fails loudly at construction rather than at the table.
- Make a passed save survive. Treat any passed save as negating a kill, since a save-or-die save is by definition the one that saves you.
I'd take (1): it keeps the engine branch honest instead of adding a special case, and any document already carrying the combination is a document whose author didn't mean it.
Either way kills deserves a line in the TrapEffect docstring saying what a save is expected to do with it — the current text ("kills marks save-or-die forms (poison gas)") doesn't say the save interaction exists.
How it surfaced
Documenting osr-editor's trap builder, which offers both controls freely and will author the combination without complaint. The editor's guide now warns about it in prose (mmacy/osr-editor#33), which is a workaround, not a fix. If the model starts rejecting it, the editor's builder should follow by pinning on_save when save-or-die is ticked — happy to send that PR here or there once you've picked a direction.
Found against osrlib 1.3.0.
What happens
TrapEffectlets a trap set bothkills=Trueandsave.on_save="half". When it does, a character who passes the saving throw is killed anyway.Why
_resolve_trap(src/osrlib/crawl/exploration.py:780-798) branches on the save result:Only
negatesshort-circuits the victim. A passinghalfsave setshalved— which is meaningful only for the damage roll further down — and then falls straight through toeffect.kills.halvedis never consulted again on that path, so the save has no effect at all.The result is a trap that reads as "save vs. death or die, half on a successful save" and behaves as "everyone the trap catches dies."
Where the fix belongs
TrapEffectalready carries a model validator holding two cross-field implications (dungeon.py:262-273: a volley needs damage dice, a duration needs a condition), so this is the same class of rule and the same place for it. Two options:kills=Truewithsave.on_save="half"to_condition_duration_needs_a_condition's sibling rules — half of a kill isn't a thing B/X expresses, so the model can refuse to represent it. This is the option consistent with how the other two implications are handled, and it fails loudly at construction rather than at the table.I'd take (1): it keeps the engine branch honest instead of adding a special case, and any document already carrying the combination is a document whose author didn't mean it.
Either way
killsdeserves a line in theTrapEffectdocstring saying what a save is expected to do with it — the current text ("killsmarks save-or-die forms (poison gas)") doesn't say the save interaction exists.How it surfaced
Documenting osr-editor's trap builder, which offers both controls freely and will author the combination without complaint. The editor's guide now warns about it in prose (mmacy/osr-editor#33), which is a workaround, not a fix. If the model starts rejecting it, the editor's builder should follow by pinning
on_savewhen save-or-die is ticked — happy to send that PR here or there once you've picked a direction.Found against osrlib 1.3.0.