feat(rules): scale healing spells with caster level

PF1e (SRD spells/cure-light-wounds.md): 'cures 1d8 points of damage +1
point per caster level (maximum +5)'; (spells/cure-moderate-wounds.md):
'cures 2d8 points of damage +1 point per caster level (maximum +10)'.
A HealEffect now carries the same DamageFlatScaling spec as damage
effects (S1b) and resolves it through effective_formula(caster_level)
at cast time, so the flat bonus grows with the caster and stops at the
stated cap. The engine passes the caster's level into _apply_heal_effect
(required keyword: no silent level-0 under-heal); Maximize Spell
maximizes the scaled formula. Cure Light/Moderate Wounds ship the SRD
values; inflict_light_wounds already carried its scaling_flat.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-09-13 17:19:58 +02:00
parent a2a80b8690
commit b84238792c
9 changed files with 290 additions and 18 deletions
+11 -7
View File
@@ -1040,10 +1040,13 @@ Règles modélisées :
total scalé suit ensuite le critique, la résistance énergétique et la
métamagie comme n'importe quelle formule (le chargeur refuse un dé de
scalage de type différent du dé de base). Absence de scalage = comportement
inchangé. Simplification documentée : le scalage ne s'applique qu'aux
dégâts (`DamageEffect`) — les soins par niveau (Cure Light Wounds
« +1 per caster level (maximum +5) ») ne sont pas scalés (la fiche dorée
épingle `1d8+1`).
inchangé. Le scalage s'applique aussi aux soins (S5b) : un `HealEffect`
porte le même `scaling_flat` (bonus plat par niveau) résolu par
`effective_formula(niveau)` à l'incantation — Cure Light Wounds
« 1d8 + 1 per caster level (maximum +5) » et Cure Moderate Wounds
« 2d8 + 1 per caster level (maximum +10) » (SRD spells/cure-light-wounds.md,
spells/cure-moderate-wounds.md) sont désormais scalés au lieu d'épinger
une formule figée.
- Sorts multi-projectiles (S2b) : un `DamageEffect` peut déclarer
`"projectiles": {"base": 1, "per_levels": 2, "at_level": 1, "max": 5}` — le
nombre de projectiles tirés vaut `base + (niveau - at_level) // per_levels`
@@ -1657,9 +1660,10 @@ Limites de sorts (conformance D6-D9, documentées plutôt que bricolées) :
(rayons), temps d'incantation `cast_time`) et types d'effets
(`DamageEffect`, `ConditionEffect`,
`HealEffect`, `BuffEffect`, `AreaEffect` — burst/cône/ligne, S1a). Un
`DamageEffect` porte des specs de scalage optionnels (`scaling` en dés,
`scaling_flat` en bonus plat) résolues par `effective_formula(niveau)` au
moment de l'incantation (S1b). Portées PF1e (personal, touch, close, medium,
`DamageEffect` (et un `HealEffect`, S5b) porte des specs de scalage
optionnels (`scaling` en dés, `scaling_flat` en bonus plat) résolues par
`effective_formula(niveau)` au moment de l'incantation (S1b). Portées PF1e
(personal, touch, close, medium,
long) calculées par `spell_range_ft`. Chargeur JSON (`load_spell`,
`load_spell_registry`) pour `data/spells/`, acceptant les codes de budget
`cast_time` et leurs alias RAW (« 1 round », « 1 minute »…). Des