5.9 KiB
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:
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:
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.