feat(combat): add withdraw action (double move with AoO protection on first square)

This commit is contained in:
2026-08-17 22:49:50 +02:00
parent 7543c55ed4
commit 5b1957152c
3 changed files with 172 additions and 14 deletions
+16 -9
View File
@@ -272,10 +272,9 @@ Règles modélisées :
`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) et les actions
spéciales (retraite) 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).
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).
- 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
@@ -307,7 +306,8 @@ 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) et la retraite (à venir) évitent les AoO de mouvement.
placement (5 ft, à venir) évite les AoO de mouvement ; 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
frapper la cible en mêlée, puis porte une seule attaque de mêlée à +2. La
@@ -315,6 +315,13 @@ Règles modélisées :
ligne droite doit être dégagée (pas de terrain difficile, d'obstacles ni de
créatures) ; distance minimum 2 cases (10 ft). `default_policy` choisit la
charge quand l'ennemi est hors de portée mais joignable en ligne droite.
- Retraite : action à round complet. Le combattant se déplace jusqu'à 2× sa
vitesse en s'éloignant de l'ennemi le plus proche (ascension gloutonne du
champ de coût Dijkstra depuis la menace). La case de départ n'est pas
considérée comme menacée — aucune AoO en la quittant. Les cases
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).
- 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
@@ -326,8 +333,8 @@ 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, actions spéciales
(pas de placement, retraite) et actions immédiates hors-tour.
- Tailles Large+ (2×2), allonge > 5 ft, pas de placement (5 ft)
et actions immédiates hors-tour.
## Architecture
@@ -361,7 +368,7 @@ appliquées dans la résolution) :
La gate de validation complète (tests + lint + types) :
```bash
uv run pytest -q # 213 tests
uv run pytest -q # 215 tests
uv run ruff check src tests
uv run basedpyright src # mode strict
```
@@ -376,7 +383,7 @@ 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 et attaques d'opportunité sont déjà modélisés.
outrance, attaques d'opportunité, charge et retraite 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