conformance: block 5-foot step on difficult terrain (SRD Take 5-Foot Step)

A 5-foot step is hampered by difficult terrain: stepping out of OR into a
difficult cell is blocked. _step5 (auto-directed) logs 'wait (cannot step)';
_step5_to (explicit destination) rejects with unreachable_destination.
Engine-level tests cover destination, auto-path, and origin cases.
This commit is contained in:
2026-09-16 13:47:10 +02:00
parent 7fc7a1d6ec
commit 437507ee2c
2 changed files with 88 additions and 1 deletions
+17 -1
View File
@@ -6898,8 +6898,17 @@ class CombatEngine:
if self.has_condition_flag(state, "cant_move"):
self._log(f"round {self._current_round} {state.combatant.id}: wait (cannot move)")
return
# SRD "Take 5-Foot Step": "You can only take a 5-foot-step if your
# movement isn't hampered by difficult terrain or darkness." — a step
# out of OR into a difficult cell is blocked (darkness not modeled).
if self._footprint_touches_difficult(self._footprint_at(state.pos, state)):
self._log(f"round {self._current_round} {state.combatant.id}: wait (cannot step)")
return
path = self._move_path(state, target)
start = state.pos
if self._footprint_touches_difficult(self._footprint_at(path[0], state)):
self._log(f"round {self._current_round} {state.combatant.id}: wait (cannot step)")
return
state.pos = path[0]
state.moved_this_turn = True
self._log(
@@ -6921,7 +6930,14 @@ class CombatEngine:
# corner (even by taking a 5-foot step)."
diagonal = state.pos[0] != destination[0] and state.pos[1] != destination[1]
corner_clear = not diagonal or self._grid.diagonal_allowed(state.pos, destination)
if not adjacent or not free or not corner_clear:
# SRD "Take 5-Foot Step": "You can only take a 5-foot-step if your
# movement isn't hampered by difficult terrain or darkness." — a step
# out of OR into a difficult cell is blocked (darkness not modeled).
step_clear = not (
self._footprint_touches_difficult(self._footprint_at(state.pos, state))
or self._footprint_touches_difficult(self._footprint_at(destination, state))
)
if not adjacent or not free or not corner_clear or not step_clear:
self._log(
f"round {self._current_round} {state.combatant.id}: "
f"{kind} rejected ({_REASON_UNREACHABLE_DESTINATION})"