feat(rules): carried items + real steal transfers, unarmed-disarm auto-pickup
- Combatant.carried_items (APG Steal: non-wielded, non-closely-worn gear), loaded from JSON, seeded into CombatantState, exposed in the snapshot. - _maneuver_steal: success transfers the first carried item victim -> thief (logged); empty list keeps the documented nothing-stealable behaviour. - _maneuver_disarm: unarmed attacker's success auto-picks the dropped weapon into its carried_items (SRD Disarm); armed disarms unchanged. Dropped-weapon recovery via retrieve_weapon (move action) verified + pinned by tests. - data/pcs: gear transcribed from Foundry sheet inventories (esha 13, jeanne 3, misty 9, nairda 7, oni 6, tammara 20); harvie/ierlieth/ esha_mount and all monsters empty (no stealable inventory), noted in README.
This commit is contained in:
@@ -299,7 +299,14 @@ Le `reach_ft` d'une attaque naturelle (morsure, sabots) n'est **pas** une arme
|
||||
à allonge : il encode l'allonge naturelle de la créature, qui reste un disque.
|
||||
`spell_resistance` (entier), `resistances` (dict type → montant) et
|
||||
`immunities` (liste de types) sont optionnels.
|
||||
Deux monstres d'exemple sont fournis : gobelin et orc.
|
||||
Le tableau `carried_items` (optionnel, défaut vide) énumère les **objets
|
||||
transportés non maniés et non portés serré** — potions, scrolls, outils,
|
||||
bourses, clés, vivres… — seuls objets volables par la manœuvre « Steal » de
|
||||
l'APG (voir « Manœuvres de combat »). Les armes maniées vivent dans
|
||||
`attacks` (elles se prennent par disarm, pas par steal) ; l'équipement porté
|
||||
serré (armure, sac à dos, vêtements, anneaux) ne peut pas être volé et n'a
|
||||
pas à être encodé — la CA des JSON reste une valeur finale. Deux monstres
|
||||
d'exemple sont fournis : gobelin et orc.
|
||||
|
||||
### Fiches de personnages (PJ)
|
||||
|
||||
@@ -318,7 +325,17 @@ uv run pf1e-sim --map data/maps/sample_arena.yaml \
|
||||
|
||||
Les armes placeholder des fiches sans dégâts encodés sont omises (notées
|
||||
dans `notes` le cas échéant) ; le champ `source` de chaque fichier pointe
|
||||
vers la fiche Foundry d'origine.
|
||||
vers la fiche Foundry d'origine. Les `carried_items` (objets volables par
|
||||
« Steal ») sont transcrits des inventaires `inventory.items` des fiches
|
||||
Foundry (esha 13, jeanne 3, misty 9, nairda 7, oni 6, tammara 20), filtrés
|
||||
conformément à l'APG : armes maniées (déjà dans `attacks`), équipement porté
|
||||
serré (armures, anneaux, amulettes, capes, ceintures, wayfinders, vêtements,
|
||||
sacs à dos) et contenants portés exclus. Trois fiches n'ont **aucun** objet
|
||||
volable : `harvie.json` (fiche sans inventaire), `ierlieth.json` (seul un
|
||||
« Backpack of Naidra » — sac à dos = porté serré, non volable) et
|
||||
`esha_mount.json` (monture sans inventaire propre) — listes laissées vides.
|
||||
Les monstres (`data/monsters/*.json`, issu du bestiaire, sans fiche Foundry
|
||||
ni inventaire encodé) portent `carried_items` vide.
|
||||
|
||||
> **`data/pcs/*.json` est la référence canonique des PJ.** Le chargeur
|
||||
> `loaders/foundry.py` est un outil de migration **one-shot et incomplet** :
|
||||
@@ -1504,7 +1521,16 @@ Règles modélisées :
|
||||
trip → `prone` ; disarm → l'arme tenue est **au sol** (SRD « Disarm » :
|
||||
« your target drops one item it is carrying » — `weapon_dropped`, exclue
|
||||
de `usable_attacks()` jusqu'à un `retrieve_weapon` ; un échec de 10 ou
|
||||
plus fait tomber **l'arme de l'attaquant**) ; bull_rush → pousse la
|
||||
plus fait tomber **l'arme de l'attaquant**) ; conformance (SRD « Disarm »)
|
||||
: « If you successfully disarm your opponent without using a weapon, you
|
||||
may automatically pick up the item dropped » — un désarmement **réussi**
|
||||
par un attaquant **sans aucune arme de mêlée utilisable** (tentative à
|
||||
mains nues) saisit aussitôt l'arme tombée : elle quitte le sol et entre
|
||||
dans les **objets transportés** du désarmeur (`carried_items`, journalisé
|
||||
`unarmed disarm pickup`) — simplification documentée : l'arme saisie
|
||||
n'étant pas une `AttackSpec` du désarmeur, elle est **tenue, pas maniée**
|
||||
(pas utilisable pour attaquer) ; l'arme reste exclue des attaques de la
|
||||
victime jusqu'à son propre `retrieve_weapon` ; bull_rush → pousse la
|
||||
cible (direction sélectionnable via `Action.direction`) ; conformance L6
|
||||
(SRD « Bull Rush ») : la poussée vaut 5 ft plus « For every 5 by which
|
||||
your attack exceeds your opponent's CMD you can push the target back an
|
||||
@@ -1558,12 +1584,23 @@ Règles modélisées :
|
||||
« Dirty Trick » ;
|
||||
`blinded`/`dazzled`/`deafened`/`entangled`/`shaken`/`sickened` possibles
|
||||
(liste exacte de l'APG), `nauseated` exclu car hors de
|
||||
la liste RAW, et rejeté à l'exécution avec journalisation) ; steal → le modèle ne porte
|
||||
aucun objet transporté non tenu : en application de l'APG « Steal » (« Items
|
||||
held in the hands (such as wielded weapons or wands) also cannot be taken
|
||||
with the steal maneuver—you must use the disarm combat maneuver instead »),
|
||||
une réussite ne vole rien (journalisé `nothing stealable — wielded weapons
|
||||
require disarm`) ; reposition → déplacement de la cible de 5 ft plus
|
||||
la liste RAW, et rejeté à l'exécution avec journalisation) ; steal → le
|
||||
champ `carried_items` du JSON de combattant porte les **objets transportés
|
||||
non maniés et non portés serré** (potions, scrolls, outils, bourses,
|
||||
vivres…) — seuls objets volables selon l'APG « Steal » (« any item that is
|
||||
neither held nor hidden in a bag or pack [...] Items that are closely worn
|
||||
(such as armor, backpacks, boots, clothing, or rings) cannot be taken [...]
|
||||
Items held in the hands (such as wielded weapons or wands) also cannot be
|
||||
taken with the steal maneuver—you must use the disarm combat maneuver
|
||||
instead ») : une réussite **transfère un objet** de la liste de la cible
|
||||
vers celle du voleur (journalisé `took <objet>`) ; simplifications
|
||||
documentées : le moteur prend le **premier** objet de la liste (le choix
|
||||
RAW de l'objet n'a pas de slot d'action), la main libre obligatoire et le
|
||||
+5 CMD des objets attachés (capes, armes fourrées, bourses) ne sont pas
|
||||
modélisés ; si la cible n'a **aucun objet transporté**, une réussite ne
|
||||
vole rien (journalisé `nothing stealable — wielded weapons
|
||||
require disarm`) ; les armes maniées (`attacks`) restent insaisissables
|
||||
par steal — il faut désarmer ; reposition → déplacement de la cible de 5 ft plus
|
||||
« For every 5 by which your attack exceeds your opponent's CMD, you can
|
||||
move the target an additional 5 feet » (APG « Reposition ») — `1 +
|
||||
marge//5` cases le long de la direction choisie (premier voisin libre par
|
||||
@@ -1702,7 +1739,9 @@ Règles modélisées :
|
||||
state) -> tuple[Action, ...]`). Schéma enrichi (E1-E5) :
|
||||
- Par combattant : id, side, pos, occupied_cells, hp/hp_max, active/dead,
|
||||
dying/stabilized, conditions (noms + rounds_left), resources_left (+ `spell_slot_{level}`),
|
||||
weapon_broken/weapon_destroyed/weapon_stolen/weapon_dropped, grappled_with, moved_this_turn, power_attack,
|
||||
weapon_broken/weapon_destroyed/weapon_stolen/weapon_dropped, carried_items
|
||||
(tuple dans l'ordre de la liste — les objets transportés volables, APG
|
||||
« Steal »), grappled_with, moved_this_turn, power_attack,
|
||||
name, level, size, natural_reach_ft (S4c : allonge naturelle du gabarit
|
||||
en pieds — 0 pour Tiny/Diminutive/Fine, 5 pour Small/Medium, 10 pour
|
||||
Large… ; l'allonge effective d'une attaque reste `max(allonge naturelle,
|
||||
|
||||
Reference in New Issue
Block a user