# RoM native Priest healing source adapter This is a local RuneWaker source implementation for native RoM healing. It is not a Chronicles of Arcadia formula, and it does not infer a spell rank from a source catalog value. `MagicProc.MagicLv` is a required runtime input. The simulator may explicitly map its trained skill level to `clamp(characterLevel, 1, 80)`; that is simulator policy, not a claim about the source database's configured `MagicLV` field. ## Direct HolyHeal `romNativeHealing.ts` resolves the `MagicProcess.cpp` `AtkCalType::HolyHeal` branch. The source equation is: ```text OrgPower = DmgPower + DmgPower * DmgPower_SkillLVArg * MagicLv / 100 SpellTime = MagicCol.SpellTime; use 1 when below 1 or IsFixDmgEffect is true MNDPart = MND * 0.3 * SpellTime Power = OrgPower * 6 when OrgPower * 5 < MNDPart; otherwise OrgPower + MNDPart unless IsIgnoreDmgEffect: add MDMG * FIX.Dmg_HealArg, once when fixed or multiplied by SpellTime otherwise Power = Power * MPow * HealPowerRate + HealPoint * SpellTime per target, apply Atk.Rand variation unless MagicBase.SettingEx.IgnoreMagicAbsorb: Power = Power * (1 + HealAbsorbRate / 100) + HealAbsorb clamp Power below zero; reverse sign for undead; suppress negative undead output out of combat ``` The generated source map binds ability and rank identity to raw `MagicCollect` rows and direct HP `MagicObject` components. It preserves source component IDs, `AtkCalType`, signed `DmgPower`, its skill-level argument, fixed fields, random range, critical rate, `SpellTime`, range/sharing data, and the distinct `MagicCollect` timing flags and mode flags. `holyHealBindings` is the 18-row subset with `AtkCalType == 5`; `attackBindings` retains all 225 direct HP ability rows for shared combat integrations. The Priest bindings include Heal (`490261`, `DmgPower 90`, argument `30`, `SpellTime 2`), Group Heal (`490281`, `DmgPower 65`, argument `20`, `SpellTime 3`), and Urgent Heal (`491147`, `DmgPower 35`, argument `20`, `SpellTime 1`). Their exact component and rank records are in the generated metadata. The helper expects already-effective native caster and recipient channels; callers must not run generic healing modifiers a second time when those modifiers were included in the effective inputs. ## Periodic HP healing Runewaker has a separate native HoT path. It is not a HolyHeal cast repeated each tick. `MagicProcess.cpp` computes the applied buff duration and invokes `CalAddDotMagicLv`; `RD_Normal.cxx` calculates the buff's source rank correction; `RD_ValueCal.cxx::BuffProc` uses the frozen `Buff.Power` at each tick. The helper supports active positive magical `DotMagic` with HP type 0, including Priest's Regenerate (`490269`). It deliberately returns no result for unsupported `DotMagic` types, including percentage HP type 5. At application, the source first derives actual buff duration from `EffectTime`, `EffectTime_SkillLVArg`, `DLV_EffectTime`, and owner/target levels, then uses `DotTimes = max(1, int(EffectTime / Dot.Time))`. For a positive magic HP dot, the source calculates: ```text DotBase = Dot.Base * (1 + MagicLv * DotSkillLVArg / 100) ExRate = 0.6 + abs(DotBase) / 600 for an IsPlayer owner; otherwise 1 Power = DotBase + ExRate * MDMG * int(SpellTime) * FIX.Dmg_HealDotArg / DotTimes MNDPart = MND * 0.3 * int(SpellTime) / DotTimes Power = Power * 6 when Power * 5 < MNDPart; otherwise Power + MNDPart Power = Power * HealPowerRate + HealPoint * int(SpellTime) / DotTimes unless IgnoreMagicAbsorb: Power = Power * (1 + HealAbsorbRate / 100) + HealAbsorb / DotTimes clamp Power below zero; Power *= MagicPow(MagicType) MagicLvAdjustment = int((Power - Dot.Base) / (Dot.Base * DotSkillLVArg / 100) - MagicLv) Buff.Power = MagicLvAdjustment + MagicLv ``` `FixDotPower` or a zero `DotSkillLVArg` bypasses the rank correction, leaving `Buff.Power = MagicLv`. The source tick then computes `Damage = int(-Dot.Base * (100 + DotSkillLVArg * Buff.Power) / 100)` and applies `AddHP(-Damage)`. C++ integer conversion truncates toward zero. Recipient absorb and caster channels are captured through the buff-rank correction at application; the source does not reapply them on each tick. The helper returns the resulting integer tick amount and the `DotTimes` interval count so the runtime can preserve that application snapshot. The live helper requires an explicit effective `MagicPow(MagicType)` value. The existing native stat sidecar has an all-magic neutral MPow channel but does not expose all six element-specific MPow `BodyRate` channels. The caller must supply an effective source value when element-specific modifiers are modeled. For this simulator's active player-build identities, `sourceIsPlayer=true` is an explicit policy applied to both the active player and party companions; the original `IsPlayer()` test itself distinguishes actual server player roles. The current Priest Regenerate source row contains positive HP DotMagic components `500469` (argument 18), `621318` (argument 18), and `621548` (argument 25). They share `Dot.Time 2`, `Dot.Base 20`, and duration 20 seconds. The generated map retains each component ID so the runtime can use the component selected by its own source semantics instead of guessing which raw component is active. ## Source snapshot and limits The generated artifact is `src/game/generated/romNativeHolyHealBindings.generated.json`; regenerate it with `scripts/runewaker-pipeline/export-rom-native-healing.mjs`. It is tied to the current hashes of `magiccollectobject.db`, `magicobject.db`, the local server formula files, the player catalog, and generated native stat rules. The output includes 225 direct-attack ability rows, 18 HolyHeal ability rows, and 8 positive magical HP DotMagic ability rows across the catalog. The data and formula boundary is the local RuneWaker source version hashed in that artifact. This work does not add CoA rank data, model percentage HP periodics, or infer unavailable source skill levels from `configuredCollectMagicLevel`.