34 lines
9.8 KiB
Markdown
34 lines
9.8 KiB
Markdown
# RuneWaker Druid Source Follow-up
|
||
|
||
Updated 2026-10-02. This is a read-only continuation of [the remaining-proc audit](ROM_DRUID_REMAINING_PROC_AUDIT.md). It follows native collection conditions, callback dispatch, and the corresponding LearnMagic rows. It does not repeat the prior PlotName graph audit or substitute later CoA identities. No runtime files were changed and no tests were run.
|
||
|
||
## Native dispatch findings
|
||
|
||
`MagicCollectCondition` stores a check function, component ID, and two arguments (`../Runewaker/SourceCodeVS2019/Libraries/Union/RoleData/ObjectStruct.h:4389-4393`). The source enum is Normal=0, FirstGroupHit=1, PrvGroupHit=2, Vampire=3, Vampire1=4 (`ObjectStruct.h:4412-4418`). In `magiccollectobject.db`, the mode int32 is at record `+0x2C8`: 499935 record `0x9C7304` has bytes `00 00 00 00`; 499497 record `0x958D54` has `01 00 00 00`; 499942 record `0x9C8F3C` has `03 00 00 00`. These offsets pin the mode independently of the condition rows. `Check_Rand` selects a surviving target when `rand() % 100 < RandValue` and skips deleted targets or targets already resolved in that group (`../Runewaker/SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp:4905-4918`). The engine resets `IsSpellOneMagic` at each group, runs each condition in order, and dispatches its component after the check; group target transitions are handled in the same loop (`MagicProcess.cpp:1390-1534`). Therefore ordered 60/30 rows are not a combined-roll estimate.
|
||
|
||
`MagicBase.CheckUseLua` is called after the collection condition and before the component effect. The Lua guard only evaluates HIT targets; a false return removes that target from the pending effect list (`MagicProcess.cpp:5496-5518`). `EndUseLua` runs from effect handlers after the result is known. `MagicCollect.CheckUseScript` is a separate cast-use check (`MagicProcess.cpp:187-212, 935-962`). The targeted rows have no such collection script. The `MagicCollect.LUAScript` member is declared in `ObjectStruct.h:4489-4497`; no consumer for that field was found in the targeted NetGameServer source. The global Lua VM loader loads FDB or configured text scripts and calls `Initialize`; the split loader also loads `C:\Runewaker\Resource\luascript` (`../Runewaker/SourceCodeVS2019/Server/NetGameServer/MainProc/Global.cpp:219-285, 301-325`). `Magic07_RunPlot` dispatches a component's `RunPlot.PlotName` on HIT or CRITICAL (`MagicProcess.cpp:4719-4743`).
|
||
|
||
The raw event-trigger slots were checked on the linked Revival Slash components and the Inspiration child payloads. `623008` has `OnAttack` tuple `[10,0,0]` (rate with no trigger ID); its on-time, on-hit, timeout, rebound, and death IDs are zero. `623339` and `623340` likewise have no event IDs. The 499942 delay buffs are the exception: both carry `OnBuffTimeOutMagicID=494344`. Spring Flower's stack buffs carry a separate `OnTimeMagicID` expiry event, detailed below. `BuffProc` decrements the timer, applies periodic damage/healing ticks, then queues the timed magic at a matching remaining-time divisor before removing an expired buff (`../Runewaker/SourceCodeVS2019/Libraries/Union/RoleData/RD_ValueCal.cxx:3456-3470, 3607-3622`). Thus the numeric proc chances below come from collection Rand fields, not an inferred callback or plot-name convention.
|
||
|
||
## Source-backed candidate rows
|
||
|
||
| RuneWaker mechanic | Exact local evidence | Status for runtime work |
|
||
| --- | --- | --- |
|
||
| **Warrior/Druid Revival Slash 499496 / hidden child 499497** | `learnmagic.db` has Warrior primary entry 128: 499496, level 60, key 546742, prerequisite 493528; general entry 28 is hidden 499497 with prerequisite 499496. Child 499497 is `FirstGroupHit` mode (raw record `0x958D54`, mode field `+0x2C8` = 1). Group 0 requires the original hostile target's hit to survive; misses/zero-value failures delete that target. Groups 1 and 2 retain the original target, with later hit types forced to HIT (`MagicProcess.cpp:1425-1459, 1544-1558, 2932-2954`). Group 2 is one `Rand 25 -> 623008` check. Its plot calls `Cl_JOB1_8_Heal_Decapitate`, which self-casts 499883 at secondary-job Magic Power level; the separate CheckUseLua only guards against active 623340 or Recover 503797 (`../Runewaker/Resource/luascript/02707.lua:43-58`). | **Chance resolved: 25%, once on the surviving original target per cast.** The hidden heal is not yet a complete runtime formula. Components 623339/623340 use HolyHeal base 30 and periodic base 15 (skill arguments 20/18); normal Recover 503796/503797 uses 40/20. Native HolyHeal also uses MND, MDMG, MPow, HealPowerRate, and HealPoint (`MagicProcess.cpp:2636-2655`). Do not scale ordinary Recover by 0.75. The current runtime has no targeted 499496/499497/499883 handler. |
|
||
| **Druid/Scout Spring Flower Eulogy 494932 / active 499935** | `learnmagic.db` Druid entry 71 is 499935, level 50, key 543156, prerequisite 494932; `02248.lua:164-166` maps Druid/Scout level 50 to key 543156. Its Normal group mode is 0 (raw record `0x9C7304`, mode field `+0x2C8`). Group 0 is an ordered target-buff ladder: 623115 if target has 623115, otherwise 623115 if it has 623111, otherwise 623111 if it has 623109, otherwise 623109. Group 1 is `Rand 30 -> 505742`; 505742 binds `lua_star_Skill_NatureBase(0, 1)` in `01465.lua:145`, granting one Nature Power. The similarly named `Lua_Skill499935_main` function in `02708.lua:125-143` is not bound to the inspected component. | **Proc chance and expiration effect resolved:** 30% for +1 Nature Power on learned/cast 499935, plus a bound 1/3/5% maximum-HP end heal. The stack buffs have six-second duration (`EffectTime_SkillLVArg=0`) and one-second Dot cadence, with bases 15/25/35 and DotSkillLVArg 16; native tick scaling depends on MDMG, MND, HealPowerRate, HealPoint, MPow, and target healing absorption (`RD_Normal.cxx:2423-2501`). Each tier's OnTimeMagic is scheduled at interval six: 623109 -> collection 494952 -> component 505706 (FixPer HP 1); 623111 -> 494953 -> 505740 (FixPer HP 3); 623115 -> 494954 -> 505741 (FixPer HP 5). The final DoT tick and end heal occur when the six-second buff reaches zero. The below-50% Lua helper remains unbound. This is a source-backed candidate tied by LearnMagic prerequisite to 494932. The current generated player catalog has no 499935 row, so HealerMan has no invocation or runtime approximation for it yet. |
|
||
| **Druid/Mage Inspiration Display 499941 / hidden 499942** | `learnmagic.db` Druid entry 91 is 499942, level 70, key 546840, prerequisite 499941. Its `Vampire` group mode is 3 (raw record `0x9C8F3C`, mode field `+0x2C8`); the later group target redirects to the owner after an earlier target survives. Group 2 is ordered `Rand 60 -> 623123`, then `Rand 30 -> 504890`. A successful first assist marks the target resolved and suppresses the fallback. 623123 is gated by `lua_bico_dru_mag_es_70` checking Spiritual Gush buff 505163 (`02708.lua:154-161`); if that guard rejects the first effect, the second check can still run. Both one-second special buffs time out to collection 494344 (`../Runewaker/SourceCodeVS2019/Libraries/Union/RoleData/RD_ValueCal.cxx:3042-3070`), whose effect is component 504863: 10 seconds, modifier type 51 (`SpellSpeed_AllMagic`) at -1000 (`ObjectStruct.h:1427-1478`). | The hidden row and its learned prerequisite are source-backed. This establishes the child’s ordered 60% gated effect / 30% fallback and timeout payload. It does **not** establish an automatic cast from public Inspiration 494322 or an automatic trigger from Spiritual Gush 494370; 505163 is only the 623123 gate. Model it, if selected, as the 499942 variant with this exact order and payload. The current generated player catalog has no 499942 row. |
|
||
|
||
## Remaining unresolved mechanics
|
||
|
||
- **Quick Light Protection 494924:** its passive row has no component conditions; active Blind Spot 490311 has ordered SelfEqType checks but no Rand chance. The proc probability, heal amount, and recipient tie-break remain unknown.
|
||
- **Boiling Rock 494565:** its passive has no component conditions; active Magma Blade 494564 has no Rand condition. The MP-restoration chance and amount remain unknown.
|
||
- **Avalanche 494029:** its passive has no component conditions; Rockslide 493543 has no Rand condition. The local extra-damage and Nature Power proc payloads remain unbound.
|
||
- **Extended Recovery 494200:** neither its passive row nor Recover 493528 exposes a Rand condition. The existing runtime approximation at `src/game/romDruidOverrides.ts:162-163` adds six seconds; the separate Nature Power chance remains unknown.
|
||
- **Spring Flower Eulogy 494932:** its passive row has no group condition. The sourced 499935 Rand30 clause above is usable as the learned child mechanic; other passive clauses still have no bound numeric chance in the inspected rows.
|
||
|
||
These checks cover the named parents, active rows, learned hidden children, and linked effect objects only. RuneWaker remains separate from the later CoA reworks recorded in the original audit.
|
||
|
||
## Healing-input boundary
|
||
|
||
The RuneWaker stat enum identifies MND as stat type 5 (`ObjectStruct.h:1427-1434`), and the HealerMan exporter maps source stat type 5 to `wisdom` (`scripts/runewaker-pipeline/export-rom-heirloom-reference.mjs:23-27`); `src/game/romStats.ts:17-37` exposes that base value. This is a verified base-stat alias, but the targeted RoM combat path does not provide the final effective MND value consumed by the heal formulas. The direct HolyHeal formula also needs native MDMG, the cast's Magic Power level, MPow, HealPowerRate, HealPoint, and target HealAbsorbRate/HealAbsorb (`MagicProcess.cpp:2640-2674`). Periodic heal scaling uses the analogous caster values and target heal absorption through `CalAddDotMagicLv` (`../Runewaker/SourceCodeVS2019/Libraries/Union/RoleData/RD_Normal.cxx:2423-2501`). No verified RoM-runtime aliases were established here for those final values. Keep the Revival Slash heal and Spring Flower periodic tick amounts blocked until those mappings exist; the Spring Flower expiry percentages are separately source-defined.
|