# Aeshnidae.Healing

**Heals that keep up with the vital they heal** (Tom, 2026-09-29: healing kits, stamina kits and
Heal/Revitalize spells should be percentage based - "if health ever gets into the thousands those
healing spells become useless"; "let's not touch harm yet").

## The rule

Every heal this covers is its **retail amount x max(1, the target's maximum of that vital /
the reference)** - retail or a share of the pool, whichever is larger:

| Setting | Value | What it means |
| --- | --- | --- |
| `ReferenceHealth` | 400 | a pool of 400 health or less heals exactly as retail; 800 heals twice retail's figure |
| `ReferenceStamina` | 500 | the same for stamina (stamina kits, Revitalize) |
| `ReferenceMana` | 500 | only if `ScaleMana` |

The references sit just above a fully buffed level-275 character (live shard, 2026-09-29: Endurance
250, Self 200-290, 196 ranks in each vital - about 350-420 health, ~500 stamina, ~550 mana), so
**nobody playing today heals any differently**. The heal only grows once a pool passes the
reference, and from there the same kit or spell always restores the same share of the pool.

- **Kits** (`Healer`): the amount is multiplied and capped at what is missing (never below what
  retail would have said). The skill check - (Healing + kit bonus) x trained/spec against missing
  vital x 2 - is measured against the missing vital *divided by* the factor, so a big pool heals
  with retail's odds instead of failing nearly always. The healer's stamina cost stays retail's.
  Skill-use experience follows the (scaled) difficulty.
- **Spells**: any spell whose boost range is positive (Heal, Revitalize, their Other and
  Fellowship forms) landing on a player. ACE's `HandleCastSpell_Boost` marks the target and vital,
  and its own `UpdateVitalDelta` calls are rewritten to go through `Scaling.BoostVitalDelta`, which
  multiplies the one positive delta, once. The chat line shows the scaled amount.
- **Not touched:** harm and drain (negative boosts) - Tom, not yet; transfer spells (already a
  proportion); regeneration; potions and food; mana (`ScaleMana` false); heals landing on monsters
  or NPCs, whoever casts them (`ScaleMonsterHeals` false - tier-scaled monsters keep retail
  self-heals, and a player healing a monster heals it retail).

## Settings

`Enabled`, `ReferenceHealth`, `ReferenceStamina`, `ReferenceMana`, `ScaleKits`, `ScaleSpells`,
`ScaleMana`, `ScaleMonsterHeals`, `MaxFactor` (the most a heal is ever multiplied by; 0 = no
limit). Documented at the top of `Settings.cs`. `healing reload` re-reads the file.

## Commands (Admin, console-safe)

- `healing` - the settings, how many kit and spell heals were scaled since load, and every online
  player's maximum health/stamina/mana with the factor their heals get.
- `healing reload` - re-read `Settings.json`.
- `healing selftest [wcid]` - **staging**: runs ACE's own heal path on a creature that is never
  placed in the world (default Drudge Vagabond 11989, given 3,000 health and stamina), retail and
  then at a factor of exactly 3; checks the four patches are attached. Heals must land at 3x
  retail's range, once per cast, and Harm Self I must not change. It swaps the settings for a few
  milliseconds, so do not run it on live. (2026-09-29 on staging: Heal Self VI retail 63..117 ->
  168..360; Revitalize Self VI 85..147 -> 300..474; Harm Self I -6..-4 both ways. PASS; again 2026-09-30 after the transpiler, with CustomSpells making Heal Self VI a flat
  500: 500 -> 1500.)
  The kits need a player online to drive; their patches are shown attached.

## Hooks

| Method | Patch | Why |
| --- | --- | --- |
| `Healer.DoSkillCheck` | prefix | missing vital / factor, so the odds stay retail |
| `Healer.GetHealAmount` | postfix | amount x factor, capped at missing, floored at retail |
| `WorldObject.HandleCastSpell_Boost` (private) | prefix + finalizer | mark the heal's target and vital; clear the mark |
| `WorldObject.HandleCastSpell_Boost` (private) | transpiler | its 3 `UpdateVitalDelta` calls go through `BoostVitalDelta`, which multiplies the marked positive delta, once |

**Why a transpiler, not a patch on `UpdateVitalDelta`** (2026-09-30): the first version prefixed
`Creature.UpdateVitalDelta(CreatureVital, int)`. It passed its self-test the day it was loaded, then
after a staging restart scaled nothing ("scaled 0 of 12", patch shown attached): the method is three
lines, the JIT inlined it into the heal's compiled body, and a patch on an inlined method never runs.
Rewriting the caller's own calls cannot be inlined around. The self-test prints how many were
rewritten (ACE has 3).

Note for anyone extending it: `Creature.GetCreatureVital` takes only `Health`/`Stamina`/`Mana` (a
`Max*` id logs an error and returns null), while a `CreatureVital` calls itself `MaxHealth` etc.;
`Scaling.Pool` maps between them.
