feat(combat): add 5-foot step (free action, no AoO, mutual exclusion with move)

This commit is contained in:
2026-08-17 22:49:50 +02:00
parent 5b8735f001
commit 94521394ef
3 changed files with 190 additions and 39 deletions
+15 -10
View File
@@ -269,12 +269,10 @@ Règles modélisées :
- Économie d'action : un tour normal donne une action standard + une action de
mouvement (ou une action à round complet), plus swift/free/immediate. La
politique renvoie une séquence ordonnée d'actions exécutées dans l'ordre ;
`default_policy` renvoie `(move, attack)` quand l'ennemi le plus proche est
hors de portée (déplacement à pleine vitesse le long du chemin Dijkstra,
puis attaque si une arme est utilisable) et `(attack,)` sinon. Les actions
immédiates (hors-tour, consomment le prochain swift) ne sont pas encore
modélisées. `default_policy` choisit la charge quand l'ennemi est hors de
portée mais joignable par une ligne droite (voir ci-dessous).
`default_policy` renvoie `(full_attack,)` si l'ennemi est en portée, `(charge,)`
si une ligne droite existe, `(5foot_step, full_attack)` si un seul pas
suffit à entrer en portée, ou `(move, attack)` sinon. Les actions immédiates
(hors-tour, consomment le prochain swift) ne sont pas encore modélisées.
- Attaques multiples par action : une arme avec `count` > 1 (« 2x Talons »)
résout `count` balayages indépendants dans la même action ; les balayages
restants sont perdus si la cible tombe (inconsciente ou morte) en cours de
@@ -306,7 +304,7 @@ Règles modélisées :
porte une AoO avant chaque tir. Une AoO par combattant par round (piste
`_aoo_used`, réinitialisée au début du round) ; les AoO sont toujours portées
(PF1e permet de les décliner, le simulateur ne le fait pas). Le pas de
placement (5 ft, à venir) évite les AoO de mouvement ; la retraite ne
placement (5 ft) ne provoque jamais d'AoO (voir ci-dessous) ; la retraite ne
protège que la case de départ (voir ci-dessous).
- Charge : action à round complet. Le combattant se déplace en ligne droite
(Bresenham) jusqu'à 2× sa vitesse vers la case la plus proche d'où il peut
@@ -322,6 +320,12 @@ Règles modélisées :
ultérieures provoquent des AoO normalement (résolues pas à pas, comme pour
le mouvement). `default_policy` ne choisit pas la retraite (disponible via
une politique personnalisée).
- Pas de placement (5-foot step) : action libre qui déplace le combattant d'une
case vers l'ennemi le plus proche sans provoquer d'AoO. Exclusivité mutuelle
avec tout autre mouvement (move, charge, retraite) dans le même tour —
tracked via `moved_this_turn`, réinitialisé au début du tour. `default_policy`
choisit `(5foot_step, full_attack)` quand un seul pas suffit à entrer en
portée.
- Distance : pénalité cumulative de 2 par incrément de portée complet au-delà
du premier, appliquée au jet d'attaque et à la confirmation de critique ;
l'arme ranged reste utilisable jusqu'à 10 incréments. Les armes de jet
@@ -333,7 +337,7 @@ appliquées dans la résolution) :
- Sorts, jets de sauvegarde, conditions et états.
- Manœuvres de combat.
- Effets mécaniques de hauteur/élévation.
- Tailles Large+ (2×2), allonge > 5 ft, pas de placement (5 ft)
- Tailles Large+ (2×2), allonge > 5 ft
et actions immédiates hors-tour.
## Architecture
@@ -368,7 +372,7 @@ appliquées dans la résolution) :
La gate de validation complète (tests + lint + types) :
```bash
uv run pytest -q # 216 tests
uv run pytest -q # 219 tests
uv run ruff check src tests
uv run basedpyright src # mode strict
```
@@ -383,7 +387,8 @@ uv run basedpyright src # mode strict
- **Phase 1** — magie et états : jets de sauvegarde, sorts modélisés comme
effets paramétrés, conditions, manœuvres de combat. Flanquement, attaques à
outrance, attaques d'opportunité, charge et retraite sont déjà modélisés.
outrance, attaques d'opportunité, charge, retraite et pas de placement sont
déjà modélisés.
- **Phase 2** — couche tactique LLM : stratégies en langage naturel traduites
en politiques, balayage de matrices de positionnement.
- **Phase 3** — rapporteur LLM local : agrégation des statistiques et