Files
healer-man/docs/ROM_NATIVE_STATS_SOURCE_2026-10-03.md
T

56 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.