feat(rules): spell components + deafened verbal failure and arcane spell failure rolls
- SpellSpec.components (frozenset of V/S/M/F/DF, JSON key accepting the RAW string notation "V, S, M/DF" or a token list; slash = alternative; unknown token is a data error). Default empty = not encoded yet -> no failure. - Combatant.armor_spell_failure (0-100 percent, authored on the JSON - the model has no equipment) and arcane_caster (divine casters never incur ASF); both load directly through load_combatant. - _cast_spell: after the start-of-casting gates and before delivery, two conditional d100 rolls (convention mirrors the concealment roll, <=): deafened + V component spoils the spell on <=20 (SRD cast-a-spell.md); arcane_caster + S component + armor_spell_failure > 0 fails the spell on <= the ASF percent (SRD arcane-spells-and-armor.md). The slot stays spent (mirrors the concentration fizzle). No condition -> no roll: the RNG stream is bit-identical for every current combat (registry encodes no components yet - full encoding is a follow-up task). - Observation: spell summary gains "components" (sorted tuple), combatant snapshot gains "armor_spell_failure"/"arcane_caster"; README documents the keys and the two former out-of-scope bullets now describe the modeled rule with its data-encoding follow-up. - Data smoke test: lich arcane_caster true / ASF 0, oni arcane_caster false. - tests/test_spell_failure.py: 21 tests (parsing, thresholds <=20 / <=ASF, divine/no-ASF/no-component never roll, roll order, slot stays spent, observation keys). Golden transcripts unchanged.
This commit is contained in:
@@ -1691,7 +1691,15 @@ Règles modélisées :
|
||||
Large… ; l'allonge effective d'une attaque reste `max(allonge naturelle,
|
||||
reach_ft de l'arme)`), aware (SRD « Surprise » : agit au round de surprise),
|
||||
alignment (SRD « Alignment » : code à deux axes parmi LG/NG/CG/LN/N/CN/LE/NE/CE,
|
||||
défaut "N"),
|
||||
défaut "N"), armor_spell_failure (SRD « Arcane Spells and Armor » :
|
||||
pourcentage 0-100 d'échec d'incantation arcanique dû à l'armure, authored
|
||||
sur le JSON — le modèle n'a pas d'équipement) et arcane_caster (bool :
|
||||
vrai = incanteur arcanse exposé à l'ASF, faux = incanteur divin jamais
|
||||
exposé), armor_spell_failure (int 0-100 — pourcentage d'échec
|
||||
d'incantation arcanique de l'armure, SRD « Arcane Spells and Armor »,
|
||||
authored sur le JSON faute de modèle d'équipement) et arcane_caster
|
||||
(bool — vrai pour un incanteur arcanse exposé à l'ASF, faux pour un
|
||||
incanteur divin),
|
||||
dr {amount, bypass} ou None (C1), creature_type (C3),
|
||||
bab (E1), initiative_mod (E2) et initiative_total (U2 : total du jet
|
||||
d'initiative courant = d20 + mod, 0 si pas encore de clé — invocation
|
||||
@@ -1716,7 +1724,9 @@ Règles modélisées :
|
||||
radius_ft, centered_on_caster} | None (U7a : premier AreaEffect de niveau supérieur du sort,
|
||||
gabarit de zone structuré, None si le sort n'a pas de zone), cast_time
|
||||
(code de temps d'incantation, « standard » pour un sort inconnu du
|
||||
registre)),
|
||||
registre), components (tuple trié des tokens de composantes V/S/M/F/DF,
|
||||
SRD « Cast a Spell — Spell Components » ; vide = composantes non
|
||||
encodées — aucun échec de composante possible pour ce sort)),
|
||||
spell_resistance (int), resistances {type: montant}, immunities
|
||||
(tuple triée des types immunisés), sneak_dice (dés de précision disponibles :
|
||||
(niveau + 1) // 2 avec « Sneak Attack », sinon 0), engaged_in_melee (bool —
|
||||
@@ -1844,27 +1854,35 @@ plutôt que bricolées) :
|
||||
large ones, may present an obstacle even when helpless. In such cases, each
|
||||
square you move through counts as 2 squares » — les cases d'une créature
|
||||
inactive ne bloquent pas et ne coûtent pas double).
|
||||
- **Échec d'incantation pour un personnage sourd hors périmètre** (SRD
|
||||
« Conditions — Deafened » : « a 20% chance of spell failure when casting
|
||||
spells with verbal components ») : le moteur ne porte pas la composante
|
||||
verbale des sorts (`SpellSpec` n'a pas de champ `components` — il
|
||||
faudrait l'encoder sur les 108 sorts du registre) et ne modélise pas non
|
||||
plus le jet de Perception DD 20 basé sur le son ni l'échec automatique
|
||||
associé ; la condition `deafened` reste donc un porteur de modificateurs
|
||||
(−4 d'initiative) sans la faillibilité d'incantation. Le soin des
|
||||
- **Composantes des sorts et échecs d'incantation associés — moteur en
|
||||
place, encodage des données en cours** (SRD « Cast a Spell — Spell
|
||||
Components » : « A spellcaster who has been deafened has a 20% chance to
|
||||
spoil any spell he tries to cast if that spell has a verbal component » ;
|
||||
SRD « Arcane Spells and Armor » : « If you cast an arcane spell with a
|
||||
somatic component while wearing armor, you incur a chance of arcane spell
|
||||
failure ») : `SpellSpec` porte un champ `components` (liste de tokens
|
||||
parmi V/S/M/F/DF, notation RAW acceptée — `"V, S, M/DF"` où la barre
|
||||
oblique sépare les alternatives) et `Combatant` porte
|
||||
`arcane_caster` (vrai pour les incanteurs arcanses — le liche ; faux
|
||||
pour les incanteurs divins — Oni) et `armor_spell_failure` (pourcentage
|
||||
0-100 authored sur le JSON, le modèle n'ayant pas d'équipement — la CA
|
||||
des JSON est une valeur finale). `_cast_spell` joue les deux jets après
|
||||
les gardes de début d'incantation (emplacement déjà dépensé — un échec
|
||||
brûle l'emplacement, comme le fumble de concentration) : d100 ≤ 20 si le
|
||||
lanceur est `deafened` et le sort a la composante V
|
||||
(journalisé `casting failed (deafened)`) ; d100 ≤ l'ASF si le lanceur
|
||||
est arcanse, porte la condition `S` et a un ASF > 0 (journalisé
|
||||
`arcane spell failure`) ; convention `<=` miroir du jet de
|
||||
dissimulation. Chaque jet n'est joué que si sa condition tient : un
|
||||
lanceur non-sourd et dépourvu d'ASF garde un flux RNG bit-identique.
|
||||
Reste hors périmètre (tâche de suivi) : l'encodage des composantes sur
|
||||
les 108 sorts du registre `data/spells/` (aucun sort ne déclare encore
|
||||
de composante, donc aucun échec ne peut partir dans les combats
|
||||
dorés) ; le jet de Perception DD 20 basé sur le son du sourd et son
|
||||
échec automatique associé ne sont pas modélisés. Le soin des
|
||||
morts-vivants par les sorts de soin (`cure`) est en revanche modélisé :
|
||||
SRD « Cure Light Wounds » — « this spell deals damage to them instead of
|
||||
curing their wounds [...] can attempt a Will save to take half damage ».
|
||||
- **Échec d'incantation arcanique due à l'armure (ASF) hors périmètre** (SRD
|
||||
« Arcane Spells and Armor » : « Armor restricts the complicated gestures
|
||||
required while casting any spell that has a somatic component. The armor
|
||||
and shield descriptions list the arcane spell failure chance for different
|
||||
armors and shields ») : le moteur ne modélise pas l'équipement — le
|
||||
`Combatant` n'a aucun champ armure/bouclier (la CA des JSON est une valeur
|
||||
finale, voir « Taille et attaques/CA ») et `SpellSpec` n'a aucun champ
|
||||
`components` (même constat que la limite « personnage sourd » ci-dessus) ;
|
||||
la probabilité d'échec d'incantation n'a donc ni source (montant d'ASF de
|
||||
l'armure) ni filtre (sorts à composante somatique) et n'est pas appliquée.
|
||||
- **Congé des sorts à durée « (D) » hors périmètre** (SRD « Cast a Spell —
|
||||
Dismiss a Spell » : « Dismissing an active spell is a standard action
|
||||
that doesn't provoke attacks of opportunity » ; marque « (D) » de la
|
||||
@@ -1969,7 +1987,9 @@ Limites de sorts (conformance D6-D9, documentées plutôt que bricolées) :
|
||||
- `spells.py` — sorts : `SpellSpec` (dataclass avec niveau, école, portée,
|
||||
sauvegarde, DC de base, exigence de contact à distance `requires_ranged_touch`
|
||||
(rayons), marque `harmless` (SRD « (harmless) » — sauvegarde sautée sur
|
||||
cible consentante), temps d'incantation `cast_time`) et types d'effets
|
||||
cible consentante), temps d'incantation `cast_time`, composantes
|
||||
`components` (frozenset de tokens V/S/M/F/DF, SRD « Cast a Spell — Spell
|
||||
Components »)) et types d'effets
|
||||
(`DamageEffect`, `ConditionEffect`,
|
||||
`HealEffect`, `BuffEffect`, `AreaEffect` — burst/cône/ligne, S1a,
|
||||
`OneShotEffect` — bonus compétence à usage unique sur un jet d'attaque, de
|
||||
|
||||
Reference in New Issue
Block a user