fix(combat): apply save negates to targeted spell damage effects

This commit is contained in:
2026-09-05 12:52:58 +02:00
parent cd75a4f2bf
commit 7cb3653750
4 changed files with 100 additions and 10 deletions
+11 -7
View File
@@ -364,7 +364,11 @@ Règles modélisées :
jet de concentration dont le DD dépend de la situation (`_concentration_dc`) :
`10 + dégâts + niveau` si blessé, `10 + CMB de l'adversaire + niveau` si agrippé,
sinon `15 + 2×niveau` (incantation défensive), jet = d20 + niveau + mod de CON
(+4 avec le don Combat Casting). `default_policy` choisit soin / offensif / buff via
(+4 avec le don Combat Casting). Un sort ciblé avec sauvegarde fait pleins dégâts
sur un échec et **aucun dégât sur un succès** (la sauvegarde annule) ; le
demi-dégât sur sauvegarde réussie ne s'applique que là où l'effet le déclare
(`half_on_save: true` pour les rayons/lignes/cônes, `AreaEffect.save_for_half`
pour les explosions). `default_policy` choisit soin / offensif / buff via
`_find_heal_spell`, `_find_offensive_spell` et `_find_ally_needing_heal`.
- Invocation : les créatures invoquées agissent (calées sur la case d'initiative de
leur conjurateur via `_build_round_order`) puis expirent après leur `summon_duration`
@@ -431,8 +435,6 @@ dans la résolution) :
utilisent une direction déterministe (premier voisin libre / au-delà de la
cible) ; un objet volé n'est pas récupérable en cours de combat. Une arme
brisée ne peut pas être réparée en cours de combat.
- La distribution des dégâts par type sur un sort n'envoie que des dégâts simples
(sans plage « half on save » pour les effets ciblés).
## Architecture
@@ -490,10 +492,12 @@ dans la résolution) :
conditions de peur, écoles enchantement/divination) sont dérivés des effets
pour évaluer les modificateurs conditionnés lors d'une sauvegarde. Le moteur
intègre les sorts via `Action(kind="cast_spell")` et `_cast_spell` :
résolution de sauvegarde, application des dégâts (demi-dégâts sur sauvegarde
réussie ; la RD ne s'applique pas aux dégâts de sorts), conditions, soins
(plafonnés à hp_max) et buffs. Le champ `spells` de `Combatant` contient les
noms de sorts connus ; le `spell_registry` est passé au `CombatEngine`.
résolution de sauvegarde, application des dégâts (aucun dégât sur sauvegarde
réussie pour un effet ciblé — la sauvegarde annule ; demi-dégâts seulement si
l'effet déclare `half_on_save` ; la RD ne s'applique pas aux dégâts de sorts),
conditions, soins (plafonnés à hp_max) et buffs. Le champ `spells` de
`Combatant` contient les noms de sorts connus ; le `spell_registry` est passé
au `CombatEngine`.
- `metrics.py` — statistiques en forme fermée : `win_rate`, `win_rate_sigma`,
`win_rate_band` (bande 3σ bornée à [0, 1]).
- `runner.py` — `EncounterSpec`/`Side`, `build_states` (placement en zone +