forked from phenom/healer-man
68 lines
13 KiB
Markdown
68 lines
13 KiB
Markdown
# RoM native stats source contract (2026-10-03)
|
||
|
||
This adapter is a source-derived RoM stat model for a selectable simulator ruleset. It keeps the existing shared `CharacterStats` contract available to combat code, but its `romNative` sidecar retains Runewaker semantics and raw point channels. It does not convert RoM hit, dodge, critical, or parry points to WoW ratings or percentages.
|
||
|
||
## Source records and identity
|
||
|
||
The stat exporter reads `racetable.db`, `sextable.db`, `voctable.db`, and `fixtable.db` from `F:\Projects\Runewaker\Resource\data`. Records begin at byte 140 and are 408 bytes. Vocation records contain two 408-byte records per class. The exporter validates the expected record IDs before interpreting floats and fails on non-finite values.
|
||
|
||
The player vocation argument rows are IDs 301003, 301005, 301007, 301009, 301011, 301013, 301015, 301017, 301019, and 301021 for Warrior through Champion. Playable race rows are 300000–300002 (Human, Elf, Dwarf); gender rows are 302000–302001 (male, female); the player formula/fix row is 303001. The generated output includes all ten classes, all three races, and both genders. The race and gender growth contributions are zero in these records, but their identities remain explicit inputs.
|
||
|
||
Formula definitions were checked against:
|
||
|
||
- `F:\Projects\Runewaker\SourceCodeVS2019\Libraries\Union\RoleData\RoleAttr.cpp`
|
||
- `F:\Projects\Runewaker\SourceCodeVS2019\Libraries\Union\RoleData\ObjectStruct.h`
|
||
- `F:\Projects\Runewaker\SourceCodeVS2019\Server\NetGameServer\MainProc\MagicProc\MagicProcess.cpp`
|
||
|
||
`romNativeStatRules.generated.json` records the source file hashes, row IDs, per-class/race/gender growth and coefficients, player FIX values, and formula notes. The generator also checks the Human/male base curves against `romBaseStatGrowth.generated.json`.
|
||
|
||
## Verified native formulas
|
||
|
||
At each level above 1, each base stat follows the table-defined progression `value = value * (1 + combinedPercentGrowth) + combinedFlatGrowth`. For a secondary vocation, Runewaker adds the secondary base at `Level_Sub` divided by ten. The helper accepts that secondary level; its primary-level fallback when the value is absent is simulator policy. The level-10 secondary-vocation unlock gate is also simulator policy, applied by the runtime caller rather than the source equation.
|
||
|
||
The source's MND attribute is exposed as Wisdom in the application. The player-derived channels implemented here are:
|
||
|
||
- Max HP: `(base HP + ExAddHP + STR*HpStrArg + STA*HpStaArg + flat MaxHP) * MaxHP_Rate`, minimum 1. The table adapter includes the base, attributes, flat gear, and rate; `ExAddHP` from external abilities/passives is outside this base helper.
|
||
- Max MP: `(base MP + INT*MPIntArg + MND*MpMndArg + flat MaxMP) * MaxMP_Rate`, minimum 0, when the primary or secondary class has the source MP resource type. The player `JobSpMpType` mapping gives MP to source classes 4–10 (Mage through Champion); classes 1–3 use SP. This does not replace current simulator Focus/Rage/Psi pools or change ability costs.
|
||
- ATK: `(STR*AtkStrArg + INT*AtkIntArg + AGI*AtkAgiArg + flat ATK) * ATK_Per`, minimum 1.
|
||
- DEF: `(STA*DefArg + flat DEF + armor-category base DEF contributions) * DEF_Per`, minimum 1. The helper calculates the attribute and flat-gear terms; its reference equipment curves do not contain source armor-category `BaseEQ.Def` values.
|
||
- MDEF: `(MND*MDefArg + flat MDEF) * MDEF_Rate`, minimum 1.
|
||
- MATK: `(INT*MAtkArg + flat MATK) * MATK_Rate`, minimum 1.
|
||
- DMG: `(ATK/DmgAtkArg when nonzero + flat DMG) * class/race/gender DmgRate * DMG_Per`, minimum 0. Player FIX row 303001 has `DmgAtkArg = 0`, so player gear DMG is flat.
|
||
- MDMG: `(MATK/MDmgMAtkArg when nonzero + flat MDMG) * MDMG_Per`, minimum 0. Player FIX row 303001 has `MDmgMAtkArg = 0`, so player gear MDMG is flat.
|
||
- MPow: the all-magic `BodyRate`, neutral 1. It is not MATK or MDMG.
|
||
|
||
The source's raw hit and avoidance channels are preserved as point values: physical hit adds `PhyHit_AgiArg * AGI`; physical dodge adds `PhyAvoid_AgiArg * AGI`; magical hit adds `MagHit_MndArg * MND`; magical dodge adds `MagAvoid_MndArg * MND + MagAvoid_AgiArg * AGI`. Physical/magic critical and parry are also source point channels. The sidecar does not label those values as percentages or ratings.
|
||
|
||
The verified Holy Heal inputs are source-backed, but the per-spell equation remains incomplete in this adapter. `MagicProcess.cpp` uses skill `OrgPower`, the capped `MND * 0.3 * SpellTime` contribution (cap branch gives `OrgPower * 6`), optional `MDMG * Dmg_HealArg`, then multiplies by `MPow * HealPowerRate` and adds `HealPoint * SpellTime`. Player FIX row 303001 supplies `Dmg_HealArg = 0.5`. The target-side modifier is `Power * (1 + HealAbsorbRate/100) + HealAbsorb` unless the effect is ignored. `romNative.healing` carries these coefficients and raw caster/target inputs; skill rank, `OrgPower`, timing, flags, random variance, undead behavior, and the full spell resolver remain outside this stat adapter.
|
||
|
||
## Equipment inputs and compatibility bridge
|
||
|
||
Native gear is an optional `ItemStats.romNative` payload. Its additive fields are explicitly source-semantic points; `*RateFactor` fields are multiplicative factors, with 1 neutral. `equipmentStats` combines flat values by addition and rate factors by multiplication. A source-native payload on an item takes precedence over the foreign-item bridge for that item, so mixed sets do not count its common direct values twice.
|
||
|
||
The native heirloom reader imports only `romNativeGearCurves.generated.json`, a compact five-class/slot/level table, and does not load the larger audit catalog at runtime. Those curves are source-selected Good-quality item references for Knight, Priest, Scout, Rogue, and Mage. The reference builder selects class/slot medians and selected package pieces, then applies its documented monotone level interpolation. This is simulator gear-profile policy, not a Runewaker per-level item formula. The remaining five classes and missing slots have no source-native curve.
|
||
|
||
The canonical converter keeps legacy reference aliases distinct: legacy `magicalDamage` is source type 148 MdmgPoint; legacy `magicalWeaponDamage` is type 191 MDMG; `weaponDamage` is type 25 DMG; `healingPower` is type 150 HealPoint; and `dodge` is type 17 physical dodge. The compact table therefore does not use a generic name normalizer that could map 148 into 191.
|
||
|
||
An audit of the 999 unique item IDs selected by the current five-class curves against the matching raw `armorobject.db`, `weaponobject.db`, and fixed `addpowerobject.db` records found no selected-item type 44 MPow, type 149 HealPowerRate, type 144 HealAbsorbRate, or type 147 HealAbsorb effects. Type 150 HealPoint occurs on two selected raw items and remains exported. The profile's neutral MPow/HealPowerRate factors therefore reflect absent effects on these selected references, not an assumed RoM conversion. The helper still accepts native payloads for those exact channels if a later source-specific item loader supplies them.
|
||
|
||
For items without a native payload, `romNativeGearFromSharedDirectStats` is an explicit compatibility simulator policy: it bridges only flat Strength, Stamina, Intellect, Agility, HP, and MP. WotLK Spirit is not Wisdom. WotLK ratings, Attack Power, Spell Power, armor, weapon DPS, and resistances are not interpreted as native RoM stats. Existing common item fields and their WotLK projections are preserved for compatibility.
|
||
|
||
At the shared combat boundary, runtime may use numeric native ATK as common attack power, MDMG as common spell power, and DEF as common armor under an explicit `combatStatModel` simulator label. That 1:1 adapter is for shared combat consumers; it does not assert equivalence to WoW stat definitions. MDEF and native point channels remain in the sidecar. A complete armor-defense calculation and verified native-point combat resolution are still open.
|
||
|
||
## Focused verification
|
||
|
||
`romNativeStats.test.ts` checks every class/race/gender primary curve, player-derived level-1 channels, MP gating, source healing coefficients, the 148/191 conversion boundary, a real level-70 Mage reference curve, rate-preserving aura recomputation, and gear aggregation. `equipment.test.ts` checks per-item payload precedence, foreign direct-stat bridging, mixed-loadout behavior, and native equip/unequip impact. `itemStats.test.ts` checks that native payload persistence does not change the existing WotLK numeric fields.
|
||
|
||
## Combat amount follow-up (2026-10-03)
|
||
|
||
The earlier per-spell boundary above is now partially bridged by `src/game/romNativeCombatAmount.ts`, `romNativeHealing.ts`, and `romNativeDamage.ts`. `romAbilityCatalog.ts` attaches a source skill/component key to supported native effect amounts; the runtime resolves that key against generated RuneWaker binding data. Direct HolyHeal calculation type 5 uses the source OrgPower/MagicLv and caster MND, MDMG, MPow, HealPowerRate, HealPoint inputs. The currently selected MagicLv is the character level clamped to 1–80, an explicit simulator policy rather than a source rank curve. This amount uses mean variance; the ordinary shared healing resolver continues to handle the common critical and overheal result.
|
||
|
||
The direct HolyHeal caster amount excludes target absorption fields; the source HealAbsorbRate and flat HealAbsorb are applied once for each actual recipient. DotMagic healing has its own source rank correction at application and stores its integer buff/tick amount for later pulses; player and NPC recipients reuse that same snapshot rather than reapplying the amount formula. It is not routed through the direct HolyHeal equation. The present all-magic MPow value does not establish missing element-specific BodyRate values. Unbound or unsupported components still use the shared simulator fallback.
|
||
|
||
The current direct-damage route accepts only supported active-five source components with a matching canonical training-weapon payload. `MagicAttackCalENUM` fixed types 0, 1, 2, 3, and 5–9 are implemented from the checked source enum; throw, prior-hit, and shield-category branches remain unsupported because the sidecar lacks their exact inputs. The consumer also declines `isShareDamage` components and components with nonzero `decreaseArg` on nonzero `rangeType` until exact runtime target-count and target-ordinal context is wired; the pure helper's support for these fields is not end-to-end area-damage support. Reflected Shot has a regression for this fail-closed boundary. Custom/unverified native equipment, missing source items/components, and unavailable school inputs keep the shared fallback. The returned ability amount stops at mean outgoing source power before source weapon variance, native hit/avoidance, and native target-defense handling; the existing shared target resolver remains the simulator bridge.
|
||
|
||
`nativeTrainingAutoAttackProfile` provides native physical weapon damage and intervals for player Knight main-hand attacks and NPC Knight/Rogue melee plus Scout Bow attacks; NPC Rogue offhand is also covered. Player Rogue offhand handling requires an explicitly present equipped inventory item. Player Scout Shot uses the native direct-damage route for its actual bound Shoot hit, not a continuous native bow-auto profile. Caster ambient basic-spell attacks keep the generic clock and MDMG policy. Player melee timing reads the existing common haste aura through `resolveAuraStatValue`, since native stat re-derivation does not populate the shared haste fields; the existing race-affix timing is retained. The final source-damage selection passed 13 tests: seven helper cases, two player/companion amount-parity and Reflected Shot fail-closed cases, and four actual auto/source-action cases. Native direct/periodic healing passed 14 player/NPC cases. These routes do not complete native hit/avoidance, armor categories, weapon variance, or critical resolution.
|
||
|
||
The final fixed player-rotation/native-companion diagnostic covers 80 pulls across the five requested builds at levels 1/10/30/60/65/66/79/80. All 80 targets died, with zero player/NPC deaths and zero invalid cast failures; trash took 4.5–9 seconds and Anglo 39–53.4 seconds. Native-only enemy health pacing is scoped separately from legacy WoW-stat RoM and other character origins; details and the exact bands are in [`ROM_NATIVE_COMBAT_PASS_2026-10-03.md`](ROM_NATIVE_COMBAT_PASS_2026-10-03.md). The ordinary randomized companion pool is now restricted to the active five identities; the configured factory remains broader for explicit party construction. Final production build passed with asset audits, compression, and bundle verification. Exact-dist browser QA passed with 11 screenshots captured and four representative roster, dungeon, NPC-stat, and mobile screenshots visually reviewed; current chunks were `index-D_h9IRuA.js` and `combatStore-BiA9BOQG.js`. There were no page, server, or HTTP errors and the network response-errors list was empty; the only console error was the optional existing favicon 404. A release check passed on 249 paths; the final documentation update requires the final hygiene/release check. The remaining limits are not a readiness promotion or complete balance certification.
|