docs(rules): document 3D flight over ground terrain (airborne view: difficult terrain cost 1, cross non-LoS impassable cells, blocks_los still stops flight; elevation climb still applies; LoE/cover/high-ground unchanged) and the deferred 3D line-of-effect/maneuverability with SRD citations (Movement, Position and Distance; Aiming a Spell); map legend + grid.py notes, test count 1981
This commit is contained in:
@@ -235,6 +235,10 @@ ligne d'effet ⇒ l'attaque est impossible ; couvert ⇒ bonus de CA. Le champ
|
|||||||
optionnel `cover_bonus` (`2`, `4` ou `8`) encode le niveau de couvert PF1e
|
optionnel `cover_bonus` (`2`, `4` ou `8`) encode le niveau de couvert PF1e
|
||||||
(partiel +2, standard +4, amélioré +8) ; `cover: true` sans `cover_bonus`
|
(partiel +2, standard +4, amélioré +8) ; `cover: true` sans `cover_bonus`
|
||||||
vaut couvert standard (+4), idem pour un terrain `blocks_los`.
|
vaut couvert standard (+4), idem pour un terrain `blocks_los`.
|
||||||
|
Une créature **aérienne** (vol ≥ sol, voir « Vitesse de déplacement ») ignore
|
||||||
|
les coûts de `move_cost` > 1 et **enjambe** les cases `move_cost: null` qui ne
|
||||||
|
portent **pas** `blocks_los` (fosses, gravats, piliers non bloquants) ; une
|
||||||
|
case `blocks_los` (barrière solide, hauteur supposée pleine) l'arrête aussi.
|
||||||
|
|
||||||
Sémantique de `blocks_los` — **ligne d'effet, non ligne de vision** (SRD
|
Sémantique de `blocks_los` — **ligne d'effet, non ligne de vision** (SRD
|
||||||
« Aiming a Spell — Line of Effect ») : « A line of effect is a straight,
|
« Aiming a Spell — Line of Effect ») : « A line of effect is a straight,
|
||||||
@@ -839,11 +843,43 @@ Règles modélisées :
|
|||||||
charge 2×, retraite 2×, course 4×) est le **meilleur entre la vitesse au
|
charge 2×, retraite 2×, course 4×) est le **meilleur entre la vitesse au
|
||||||
sol (`speed_land_ft`) et la vitesse de vol (`speed_fly_ft`)** — une créature
|
sol (`speed_land_ft`) et la vitesse de vol (`speed_fly_ft`)** — une créature
|
||||||
dotée d'une vitesse de vol (harvie : vol 80 vs sol 30, ierlieth : vol 40)
|
dotée d'une vitesse de vol (harvie : vol 80 vs sol 30, ierlieth : vol 40)
|
||||||
se déplace donc à sa vitesse de vol. Simplification documentée : voler
|
se déplace donc à sa vitesse de vol. **Vol au-dessus du terrain (3D)** :
|
||||||
PAR-DESSUS les obstacles ou le terrain difficile n'est PAS modélisé — la
|
une créature dont le budget vient de sa vitesse de vol (`speed_fly_ft >=
|
||||||
praticabilité et les coûts de terrain de la grille s'appliquent à tout le
|
`speed_land_ft`, `_is_airborne`) est **aérienne** et se déplace sur une
|
||||||
budget (pas de vol 3D ni de maniabilité) ; le snapshot expose ce budget
|
« vue aérienne » de la grille (`Grid.airborne_view`) : le terrain difficile
|
||||||
sous la clé additive `speed_effective_ft`.
|
au sol (coût > 1) ne la ralentit pas (coût 1) et elle **enjambe** les cases
|
||||||
|
infranchissables au sol (fosses, eau, gravats, piliers — `move_cost: null`
|
||||||
|
sans `blocks_los`), conformément au SRD « Movement, Position, and Distance »
|
||||||
|
(Terrain and Obstacles) : « Flying and incorporeal creatures are not
|
||||||
|
hampered by difficult terrain » et « Flying and incorporeal creatures are
|
||||||
|
able to avoid most obstacles ». **Exception** : une case qui **bloque la
|
||||||
|
ligne d'effet** (`blocks_los` — mur/pilier, barrière solide supposée de
|
||||||
|
hauteur pleine) **arrête aussi le vol** (elle reste infranchissable dans la
|
||||||
|
vue aérienne). La règle des coins (diagonale) suit la même vue : un
|
||||||
|
aérien peut passer en diagonale au ras d'une fosse (SRD « Measuring
|
||||||
|
Distance » : « You can also move diagonally past other impassable
|
||||||
|
obstacles, such as pits ») mais pas au ras d'un coin de mur. Les portiers
|
||||||
|
de course/charge/pas de placement (« You can't run or charge across
|
||||||
|
difficult terrain », « movement isn't hampered by difficult terrain ») ne
|
||||||
|
bloquent pas un aérien, puisque son déplacement n'est pas gêné par le
|
||||||
|
terrain. Le coût de **montée** (élévation) s'applique aussi au vol : la
|
||||||
|
couche `elevation` encode des **marches** (hauteurs de sol), non des
|
||||||
|
hauteurs de mur ou de falaise, et n'est donc pas survolable (simplification
|
||||||
|
documentée). La **ligne d'effet**, le **couvert** et l'**avantage de
|
||||||
|
hauteur** restent calculés sur la grille réelle (inchangée par le vol) :
|
||||||
|
la géométrie de combat n'est pas affectée par le statut aérien. Les
|
||||||
|
créatures non aériennes (`speed_fly_ft` absent, ou vol < sol) sont bit-par
|
||||||
|
identiques (elles paient le terrain et ne traversent pas les obstacles) ;
|
||||||
|
les transcripts dorés (arène ouverte, sol uniforme) sont inchangés. Le
|
||||||
|
snapshot expose le budget sous la clé additive `speed_effective_ft`.
|
||||||
|
**Hors périmètre (vol 3D incomplet)** : la **ligne d'effet 3D** (un vol à
|
||||||
|
l'altitude X passe au-dessus d'un mur de hauteur Y, franchit le rebord
|
||||||
|
d'une falaise) n'est **pas** modélisable — la carte ne porte aucune hauteur
|
||||||
|
de mur ni de falaise (seule la hauteur de sol `elevation` existe, et
|
||||||
|
`blocks_los` est un booléen « barrière solide », pas une hauteur) ; la
|
||||||
|
**maniabilité de vol** (turn/pitch, chute en fin de tour, `hover`) et les
|
||||||
|
autres vitesses de déplacement (natation, escalade, creusement) ne sont pas
|
||||||
|
modélisées.
|
||||||
Destination explicite : `Action(kind="move", destination=(r, c))` et
|
Destination explicite : `Action(kind="move", destination=(r, c))` et
|
||||||
`Action(kind="5foot_step", destination=(r, c))` déplacent le combattant vers
|
`Action(kind="5foot_step", destination=(r, c))` déplacent le combattant vers
|
||||||
la case `(r, c)` désignée (monter au couvert, quitter un défilé, se
|
la case `(r, c)` désignée (monter au couvert, quitter un défilé, se
|
||||||
@@ -894,7 +930,11 @@ Règles modélisées :
|
|||||||
critique (règle du high ground). Conformance vérifiée (SRD Table 8-5
|
critique (règle du high ground). Conformance vérifiée (SRD Table 8-5
|
||||||
« Attack Roll Modifiers ») : « On higher ground — Melee +1, Ranged +0 » —
|
« Attack Roll Modifiers ») : « On higher ground — Melee +1, Ranged +0 » —
|
||||||
le bonus est à juste titre réservé aux attaques de mêlée, les attaques à
|
le bonus est à juste titre réservé aux attaques de mêlée, les attaques à
|
||||||
distance ne le reçoivent pas. La ligne d'effet reste 2D (pas de LoS 3D).
|
distance ne le reçoivent pas. La ligne d'effet reste 2D (pas de LoS 3D) :
|
||||||
|
la couche `elevation` encode des hauteurs de **sol** (marches), non de murs
|
||||||
|
ou falaises — survol aérien du terrain au sol modélisé (voir « Vitesse de
|
||||||
|
déplacement »), mais la ligne d'effet 3D selon l'altitude reste hors
|
||||||
|
périmètre faute de données de hauteur de mur.
|
||||||
- Actions immédiates hors-tour : politique de réaction optionnelle
|
- Actions immédiates hors-tour : politique de réaction optionnelle
|
||||||
`reaction_policy(engine, state, event)` (défaut : aucune) ; sur événement
|
`reaction_policy(engine, state, event)` (défaut : aucune) ; sur événement
|
||||||
(`attack_resolved`, `movement_step`, `spell_cast`, `damage_taken`) une
|
(`attack_resolved`, `movement_step`, `spell_cast`, `damage_taken`) une
|
||||||
@@ -2078,6 +2118,20 @@ plutôt que bricolées) :
|
|||||||
taille — une créature Huge (3×3) est simplement bloquée par tout endroit où
|
taille — une créature Huge (3×3) est simplement bloquée par tout endroit où
|
||||||
son footprint ne rentre pas ; ni le squeeze payant, ni la réduction de
|
son footprint ne rentre pas ; ni le squeeze payant, ni la réduction de
|
||||||
footprint dans les couloirs ne sont modélisés.
|
footprint dans les couloirs ne sont modélisés.
|
||||||
|
- **Vol 3D — ligne d'effet par l'altitude hors périmètre** : le survol du
|
||||||
|
terrain au sol est modélisé (voir « Vitesse de déplacement » : terrain
|
||||||
|
difficile à coût 1, enjambement des obstacles non bloquants, barrières
|
||||||
|
`blocks_los` infranchissables, SRD « Movement, Position, and Distance ») ;
|
||||||
|
restent hors périmètre la **ligne d'effet 3D** (un vol à une altitude
|
||||||
|
donnée passe AU-DESSUS d'un mur de hauteur finie ou franchit le rebord
|
||||||
|
d'une falaise) et la **maniabilité de vol** (tourner/monter/plonger, chute
|
||||||
|
en fin de mouvement, don Hover) : la carte ne porte **aucune hauteur de
|
||||||
|
mur ni de falaise** — seule la hauteur de **sol** (`elevation`, marches)
|
||||||
|
existe, et `blocks_los` est un booléen « barrière solide » (SRD « Aiming a
|
||||||
|
Spell — Line of Effect »), pas une hauteur — donc « altitude du vol contre
|
||||||
|
hauteur de l'obstacle » n'est pas exprimable dans les données ; la ligne
|
||||||
|
d'effet reste strictement 2D et une barrière `blocks_los` est supposée de
|
||||||
|
hauteur pleine (elle arrête aussi le vol).
|
||||||
- **Passage à travers une créature (3 catégories d'écart) — approximations** :
|
- **Passage à travers une créature (3 catégories d'écart) — approximations** :
|
||||||
la règle est modélisée (traversée + AoO d'entrée, voir « Mouvement ») et le
|
la règle est modélisée (traversée + AoO d'entrée, voir « Mouvement ») et le
|
||||||
test d'Acrobatics « Tumbling » est désormais modélisé (voir « Acrobatics —
|
test d'Acrobatics « Tumbling » est désormais modélisé (voir « Acrobatics —
|
||||||
@@ -2167,7 +2221,9 @@ Limites de sorts (conformance D6-D9, documentées plutôt que bricolées) :
|
|||||||
(dimensions, caractères connus, zones, déploiement, markers).
|
(dimensions, caractères connus, zones, déploiement, markers).
|
||||||
- `grid.py` — grille 5-10-5 : `distance`, `step_cost` (terrain + élévation),
|
- `grid.py` — grille 5-10-5 : `distance`, `step_cost` (terrain + élévation),
|
||||||
règle des coins (`diagonal_allowed`), Dijkstra `reachable` avec ou sans
|
règle des coins (`diagonal_allowed`), Dijkstra `reachable` avec ou sans
|
||||||
budget.
|
budget ; `airborne_view` = vue de la grille pour les créatures aériennes
|
||||||
|
(terrain difficile/coûteux → coût 1, cases infranchissables non bloquantes
|
||||||
|
→ traversables, `blocks_los` reste bloquant).
|
||||||
- `los.py` — ligne d'effet et couvert par la règle des coins : gate de ligne
|
- `los.py` — ligne d'effet et couvert par la règle des coins : gate de ligne
|
||||||
d'effet dans `weapon_for`, bonus de couvert par niveau (+2/+4/+8 terrain via
|
d'effet dans `weapon_for`, bonus de couvert par niveau (+2/+4/+8 terrain via
|
||||||
`cover_bonus`, +4 couvert mou des créatures) dans `resolve_attack`.
|
`cover_bonus`, +4 couvert mou des créatures) dans `resolve_attack`.
|
||||||
@@ -2273,7 +2329,7 @@ Limites de sorts (conformance D6-D9, documentées plutôt que bricolées) :
|
|||||||
La gate de validation complète (tests + lint + types) :
|
La gate de validation complète (tests + lint + types) :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
uv run pytest -q # 1970 tests
|
uv run pytest -q # 1981 tests
|
||||||
uv run ruff check src tests
|
uv run ruff check src tests
|
||||||
uv run basedpyright src # mode strict
|
uv run basedpyright src # mode strict
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user