fix(rules): stop a run from crossing difficult terrain

PF1e CRB 'Movement, Position and Distance - Difficult Terrain': 'You
can't run or charge across difficult terrain.' The run action reused the
move machinery (_descend_path), so it crossed rubble at 2x cost within
the 4x budget. The descent now takes an avoid_difficult option that
refuses every step whose footprint would touch a cell with move cost > 1
(the same rejection charge applies in _charge_path_clear); the run
moves up to the obstacle and stops there (deterministic, documented).
Move and charge behavior are unchanged.

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 22:16:54 +02:00
parent 4d89305881
commit b453b19be2
3 changed files with 128 additions and 14 deletions
+11 -4
View File
@@ -693,10 +693,17 @@ Règles modélisées :
s'arrête adjacent à la cible) ou vers une `destination` explicite (U5c), en
réutilisant exactement la mécanique de `move` (descente du champ de coût
Dijkstra, pas à pas avec provocation d'AoO — courir ne protège pas des
AoO). À l'achèvement, le combattant est `running` jusqu'au début de son
prochain tour : il **perd son bonus de Dextérité à la CA** (résolu par le
même chemin flat-footed/`denies_dex_to_ac` que les conditions). Une
`destination` hors budget 4× (ou invalide) est rejetée
AoO), **sauf que la course ne traverse pas le terrain difficile**
(conformance R2 — CRB « Movement, Position and Distance — Difficult
Terrain » : « You can't run or charge across difficult terrain ») : la
descente refuse tout pas dont le footprint toucherait une case de coût > 1
(mécanisme `avoid_difficult` de `_descend_path`, symétrique du refus de la
charge dans `_charge_path_clear`) ; le combattant **avance jusqu'à
l'obstacle et s'y arrête** (comportement déterministe, retenu plutôt que
le rejet de l'action). À l'achèvement, le combattant est `running` jusqu'au
début de son prochain tour : il **perd son bonus de Dextérité à la CA**
(résolu par le même chemin flat-footed/`denies_dex_to_ac` que les
conditions). Une `destination` hors budget 4× (ou invalide) est rejetée
(`unreachable_destination`) ; sans cible ni destination, rejet
(`missing_target`). `default_policy` ne choisit pas la course (disponible
via une politique personnalisée).