docs: reorganize documentation structure
- Clean README.md with installation, usage, CLI, examples - Move data formats to docs/data-formats/ (map.md, monster.md, pc-sheets.md, index.md) - Move architecture to docs/architecture/ (index.md, technical-architecture.md, phase-2-llm-architecture.md) - Add contributing guidelines in docs/contributing.md - Clean README.md: install, usage, CLI, data-formats/rules/architecture links, contributing link
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
# Formats de données
|
||||
|
||||
- [Carte (YAML)](map.md)
|
||||
- [Monstre (JSON)](monster.md)
|
||||
- [Fiches de personnages PJ](pc-sheets.md)
|
||||
|
||||
Chaque format est documenté en détail dans son fichier respectif.
|
||||
@@ -0,0 +1,87 @@
|
||||
# Format de carte (YAML)
|
||||
|
||||
Chaque couche est un bloc ASCII de mêmes dimensions ; un caractère = une case
|
||||
de `square_size_ft` pieds. Seule `terrain` est obligatoire.
|
||||
|
||||
```yaml
|
||||
name: "Arène d'essai"
|
||||
square_size_ft: 5
|
||||
|
||||
terrain: | # couche obligatoire
|
||||
####################
|
||||
#......######......#
|
||||
#..C...######...C..#
|
||||
#......~..~........#
|
||||
#..TT..~..~...TT...#
|
||||
#......~..~........#
|
||||
#..C............C..#
|
||||
####################
|
||||
|
||||
legend: # un caractère = un type de terrain
|
||||
"#": { type: wall, move_cost: null, blocks_los: true } # bloque déplacement + LoS
|
||||
".": { type: floor, move_cost: 1 }
|
||||
"T": { type: rubble, move_cost: 2 } # terrain difficile
|
||||
"~": { type: water, move_cost: 2 }
|
||||
"C": { type: pillar, move_cost: null, cover: true } # bloque le pas, couvert (+4)
|
||||
"W": { type: ruin, move_cost: null, cover_bonus: 2 } # couvert partiel (+2) ; 4 = standard, 8 = amélioré
|
||||
|
||||
elevation: | # optionnel : chiffres = hauteur (appliqué : coût de pas + high ground)
|
||||
....................
|
||||
..1111..............
|
||||
..1111..............
|
||||
....................
|
||||
|
||||
zones: | # optionnel : lettres de zone de déploiement
|
||||
....................
|
||||
.AAAA..........BBBB.
|
||||
.AAAA..........BBBB.
|
||||
....................
|
||||
|
||||
markers: # optionnel : points nommés (validés, pas encore utilisés)
|
||||
autel: [1, 16]
|
||||
|
||||
deployment: # obligatoire si zones : nom de camp -> lettre de zone
|
||||
players: A
|
||||
monsters: B
|
||||
```
|
||||
|
||||
## Détail des champs
|
||||
|
||||
### `square_size_ft`
|
||||
Taille d'une case en pieds (entier > 0). Par défaut 5 ft (1,5 m).
|
||||
|
||||
### `terrain` (obligatoire)
|
||||
Bloc ASCII représentant le terrain. Chaque ligne = une rangée de cases.
|
||||
|
||||
### `legend`
|
||||
Dictionnaire associant chaque caractère du terrain à ses propriétés :
|
||||
- `type` : identificateur du type de terrain
|
||||
- `move_cost` : entier > 0 pour terrain praticable, `null` pour infranchissable (mur, pilier)
|
||||
- `blocks_los` : booléen, bloque la ligne d'effet
|
||||
- `cover` : booléen, couvert standard (+4)
|
||||
- `cover_bonus` : optionnel, niveau de couvert (2 = partiel, 4 = standard, 8 = amélioré)
|
||||
|
||||
**Important** : omettre `move_cost` équivaut à `null` (infranchissable). Un terrain praticable doit **toujours** déclarer explicitement son coût.
|
||||
|
||||
### `elevation` (optionnel)
|
||||
Chiffres = hauteur de sol (marche). Monter d'un niveau coûte +1 case de mouvement par niveau ; la descente est gratuite. Une créature plus haute que sa cible gagne +1 aux jets d'attaque de mêlée et à la confirmation de critique (high ground). La ligne d'effet reste 2D.
|
||||
|
||||
### `zones` (optionnel)
|
||||
Lettres de zone de déploiement. Chaque lettre = une zone distincte.
|
||||
|
||||
### `markers` (optionnel)
|
||||
Points nommés (validés à la lecture, pas encore utilisés en jeu).
|
||||
|
||||
### `deployment` (obligatoire si `zones` présent)
|
||||
Mapping nom de camp -> lettre de zone. Chaque camp du déploiement doit être fourni via `--side`, et toutes les zones doivent être couvertes. Deux camps ne peuvent pas partager la même lettre de zone.
|
||||
|
||||
## 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, unblocked path that indicates what a spell can affect. A line of effect is canceled by a solid barrier. It's like line of sight for ranged weapons, except that it's not blocked by fog, darkness, and other factors that limit normal sight. »
|
||||
|
||||
Le drapeau encode donc une **barrière solide** ; un terrain qui ne limite que la vision normale (brouillard, obscurité, dissimulation) n'est pas modélisé — le moteur n'a qu'un seul drapeau, et la dissimulation passe par les conditions (`miss_chance`), pas par la carte.
|
||||
|
||||
## Vol et terrain
|
||||
|
||||
Une créature **aérienne** (vol ≥ sol) ignore les coûts de `move_cost` > 1 et **enjambe** les cases `move_cost: null` qui ne portent **pas** `blocks_los` (fosses, eau, gravats, piliers non bloquants). Une case `blocks_los` (barrière solide, hauteur supposée pleine) l'arrête aussi. La règle des coins suit la même vue : un aérien peut passer en diagonale au ras d'une fosse mais pas au ras d'un coin de mur.
|
||||
@@ -0,0 +1,117 @@
|
||||
# Format de monstre (JSON)
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Goblin",
|
||||
"level": 1,
|
||||
"size": "Small",
|
||||
"cr": "1/3",
|
||||
"xp": 135,
|
||||
"source": "Bestiary > Goblin",
|
||||
"abilities": {
|
||||
"str_score": 11, "dex_score": 15, "con_score": 12,
|
||||
"int_score": 10, "wis_score": 9, "cha_score": 6
|
||||
},
|
||||
"hp_max": 6,
|
||||
"ac": { "total": 16, "touch": 13, "flat_footed": 14 },
|
||||
"bab": 1,
|
||||
"initiative_mod": 6,
|
||||
"speed_land_ft": 30,
|
||||
"saves": { "fort": 3, "ref": 2, "will": -1 },
|
||||
"attacks": [
|
||||
{
|
||||
"name": "short sword",
|
||||
"kind": "melee",
|
||||
"attack_bonus": 2,
|
||||
"damage": [{ "formula": "1d4", "types": ["slashing"] }],
|
||||
"crit_range": 19,
|
||||
"crit_mult": 2
|
||||
},
|
||||
{
|
||||
"name": "short bow",
|
||||
"kind": "ranged",
|
||||
"attack_bonus": 4,
|
||||
"damage": [{ "formula": "1d4", "types": ["piercing"] }],
|
||||
"crit_mult": 3,
|
||||
"range_increment_ft": 60
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Champs principaux
|
||||
|
||||
| Champ | Type | Requis | Description |
|
||||
|-------|------|--------|-------------|
|
||||
| `name` | string | oui | Nom du monstre |
|
||||
| `level` | int | oui | Niveau / HD |
|
||||
| `size` | string | oui | `Tiny`, `Diminutive`, `Fine`, `Small`, `Medium`, `Large`, `Huge`, `Gargantuan`, `Colossal` |
|
||||
| `cr` | string | oui | Challenge Rating (ex: `"1/3"`, `"5"`) |
|
||||
| `xp` | int | oui | Points d'expérience |
|
||||
| `source` | string | oui | Source (ex: `"Bestiary > Goblin"`) |
|
||||
| `abilities` | object | oui | 6 scores : `str_score`, `dex_score`, `con_score`, `int_score`, `wis_score`, `cha_score` |
|
||||
| `hp_max` | int | oui | PV maximum |
|
||||
| `ac` | object | oui | `{ total, touch, flat_footed }` — valeurs **finales** (bonus de taille déjà inclus) |
|
||||
| `bab` | int | oui | Base Attack Bonus |
|
||||
| `initiative_mod` | int | oui | Modificateur d'initiative |
|
||||
| `speed_land_ft` | int | oui | Vitesse au sol en ft |
|
||||
| `speed_fly_ft` | int | non | Vitesse de vol en ft (si absent = pas de vol) |
|
||||
| `saves` | object | oui | `{ fort, ref, will }` — jets de sauvegarde finaux |
|
||||
| `attacks` | array | oui | Liste des attaques (voir ci-dessous) |
|
||||
|
||||
## Attaques (`attacks[]`)
|
||||
|
||||
Chaque attaque est un objet avec les champs suivants :
|
||||
|
||||
| Champ | Type | Requis | Description |
|
||||
|-------|------|--------|-------------|
|
||||
| `name` | string | oui | Nom de l'arme |
|
||||
| `kind` | string | oui | `"melee"`, `"ranged"`, ou `"touch"` |
|
||||
| `attack_bonus` | int | oui | Bonus d'attaque (final, taille incluse) |
|
||||
| `damage` | array | oui | Liste de composantes : `{ "formula": "1d4", "types": ["slashing"] }` |
|
||||
| `crit_range` | int | non | Menace de critique (défaut 20) |
|
||||
| `crit_mult` | int | oui | Multiplicateur de critique (2, 3, etc.) |
|
||||
| `range_increment_ft` | int | non* | Incrément de portée en ft (> 0), requis pour `ranged` et `touch` |
|
||||
| `damage_bonus` | int | non | Bonus de dégâts plat |
|
||||
| `dr` | string | non | Type de contournement de RD (ex: `"magic"`, `"cold_iron"`) |
|
||||
| `two_handed` | bool | non | Arme à deux mains (Power Attack +3X au lieu de +2X) |
|
||||
| `reach_weapon` | bool | non | Arme à allonge (anneau, dead zone) |
|
||||
| `reach_ft` | int | non | Allonge naturelle (pour attaques naturelles) |
|
||||
| `natural` | bool | non | Attaque naturelle (pas d'itératifs, peut être `secondary`) |
|
||||
| `secondary` | bool | non | Attaque naturelle secondaire (BAB −5, Multiattack → −2) |
|
||||
| `firearm` | bool | non | Arme à feu (touch dans 1er incrément, misfire sur 1 naturel) |
|
||||
| `splash` | object | non | Arme à éclaboussures : `{ "damage": 1, "type": "fire" }` |
|
||||
| `spell_resistance` | int | non | Résistance aux sorts |
|
||||
| `resistances` | object | non | Résistances énergétiques : `{ "fire": 5 }` |
|
||||
| `immunities` | array | non | Immunités : `["fire", "poison"]` |
|
||||
|
||||
* `range_increment_ft` : entier **strictement positif** (zéro ou négatif = erreur). Pénalité −2 par incrément complet au-delà du premier. Arme `ranged` utilisable jusqu'à 10 incréments, `touch` jusqu'à 5.
|
||||
|
||||
## Champs optionnels avancés
|
||||
|
||||
- `carried_items` : array d'objets transportés non maniés/portés serré (volables via manœuvre *Steal*). Armes maniées = dans `attacks` ; équipement porté serré (armure, sac à dos, anneaux) exclus.
|
||||
- `aware` : bool (défaut `true`) — conscience au début du combat (règle *Surprise*).
|
||||
- `alignment` : code à deux axes (`LG`, `NG`, `CG`, `LN`, `N`, `CN`, `LE`, `NE`, `CE`), défaut `"N"`.
|
||||
- `spell_slots` : dict niveau → nombre (ex: `{"1": 4, "2": 3}`) — budget d'encounter, **pas** calculé depuis la caractéristique.
|
||||
- `spells` : array de noms de sorts connus (doivent exister dans `data/spells/`).
|
||||
- `metamagic_feats` : array de noms de dons de métamagie connus.
|
||||
- `features` : noms de capacités de classe / dons (résolus par le chargeur).
|
||||
|
||||
## Taille et allonge
|
||||
|
||||
| Taille | Allonge naturelle | Footprint |
|
||||
|--------|-------------------|-----------|
|
||||
| Fine / Diminutive / Tiny | 0 ft | 1×1 |
|
||||
| Small / Medium | 5 ft | 1×1 |
|
||||
| Large | 10 ft | 2×2 |
|
||||
| Huge | 15 ft | 3×3 |
|
||||
| Gargantuan | 20 ft | 4×4 |
|
||||
| Colossal | 30 ft | 6×6 |
|
||||
|
||||
Une **arme à allonge** (`reach_weapon: true`) double l'allonge naturelle mais crée une **dead zone** (ne frappe pas à l'allonge naturelle ou moins). Une **arme naturelle** avec `reach_ft` explicite conserve cette allonge (le `reach_ft` d'une attaque naturelle EST son allonge naturelle).
|
||||
|
||||
## Notes importantes
|
||||
|
||||
- Les valeurs `ac` et `attack_bonus` des JSON sont des valeurs **FINALES**, déjà majorées du modificateur de taille PF1e. Le moteur n'applique **jamais** de bonus de taille aux jets d'attaque ni à la CA.
|
||||
- La taille n'intervient que pour : manœuvres (CMB/CMD), allonge naturelle, footprint, occupation.
|
||||
- Les JSON de `data/monsters/` et `data/pcs/` sont la référence. Le chargeur `loaders/foundry.py` est un outil de migration **one-shot et incomplet** — ne pas régénérer les JSON de PJ sans réappliquer les corrections SRD (voir `docs/rules/conformance-srd.md`).
|
||||
@@ -0,0 +1,53 @@
|
||||
# Fiches de personnages (PJ)
|
||||
|
||||
Les PJ sont définis au format JSON (schéma `Combatant`) dans `data/pcs/`. Huit fiches
|
||||
sont fournies : `esha.json`, `harvie.json`, `ierlieth.json`, `jeanne.json`,
|
||||
`misty.json`, `nairda.json`, `oni.json`, `tammara.json`.
|
||||
|
||||
Elles se passent directement à la CLI comme les monstres :
|
||||
|
||||
```bash
|
||||
uv run pf1e-sim --map data/maps/sample_arena.yaml \
|
||||
--side players data/pcs/oni.json \
|
||||
--side monsters data/pcs/tammara.json --runs 200
|
||||
```
|
||||
|
||||
## Format JSON
|
||||
|
||||
Le schéma est identique aux monstres (voir [`monster.md`](monster.md)), avec
|
||||
ces particularités PJ :
|
||||
|
||||
- `source` : pointe vers la fiche Foundry VTT d'origine sous `fiches_personnages/`
|
||||
(ex: `"fiches_personnages/oni.foundry.json"`).
|
||||
- `spell_slots` : budget d'encounter (pas progression RAW — voir note ci-dessous).
|
||||
- `spells` : noms de sorts connus (doivent exister dans `data/spells/`).
|
||||
- `features` : noms de capacités de classe / dons (résolus par le chargeur).
|
||||
- `carried_items` : objets volables (filtrés depuis l'inventaire Foundry per APG *Steal*).
|
||||
|
||||
## Note importante : `spell_slots` = budget d'encounter
|
||||
|
||||
Le champ `spell_slots` est une valeur **finale** par encounter. Le moteur ne calcule
|
||||
**jamais** les sorts bonus de caractéristique d'incantation élevée (SRD Table 1-3).
|
||||
Les éventuels sorts bonus doivent être **encodés dans le JSON par l'auteur**.
|
||||
|
||||
Preuve : les fiches PJ actuelles encodent la table de classe de BASE sans bonus.
|
||||
Exemple : Oni (clerc 4, SAG 18) → `{"1":4, "2":3}` = table de base 4/3,
|
||||
alors que le RAW avec bonus serait 5/4. Le liche (INT 20, niveau 11) porte
|
||||
`{"1":4, "2":4, "3":4, "4":4, "5":4, "6":3}` qui ne correspond à AUCUNE table
|
||||
RAW — les slots sont donc un budget d'encounter authored, pas une progression RAW.
|
||||
|
||||
Conséquence assumée : les PJ incanteurs tiennent moins de sorts par encounter
|
||||
que le RAW ne le permettrait.
|
||||
|
||||
## Fiches Foundry (source)
|
||||
|
||||
`fiches_personnages/` contient les 8 fiches au format Foundry VTT
|
||||
(`pf1-sheet/v1`) ; le chargeur `loaders/foundry.py` sait les lire. C'est un
|
||||
outil de migration **one-shot et incomplet** : il ne reproduit pas les corrections
|
||||
manuelles/SRD appliquées aux JSON (drapeaux `natural`/`secondary`/`firearm`,
|
||||
allonge sabots Jeanne 10 ft, vol Ierlieth 40 ft, morsure Misty 1d6, dons au-delà
|
||||
de 8 entrées, `spell_slots`/`resources`/`casting_ability`).
|
||||
|
||||
> **`data/pcs/*.json` est la référence canonique des PJ.** Ne pas régénérer
|
||||
> depuis `foundry.py` sans réappliquer les corrections SRD consignées dans
|
||||
> [`docs/rules/conformance-srd.md`](conformance-srd.md).
|
||||
Reference in New Issue
Block a user