Files
healer-man/docs/ROM_NATIVE_DISARM_2026-10-05.md
2026-10-06 22:25:34 -04:00

9.6 KiB
Raw Permalink Blame History

RuneWaker Native Disarm Main-Hand Audit (2026-10-05)

Finding and scope

The local RuneWaker RoleAttr source initializes a missing main hand as EM_Weapon_Unarmed with speed 30, and its unarmed Main_DMG path supplies Level * 2 when the main-hand base damage is zero. This is enough to replace the training weapon inputs for native Main_DMG-dependent formulas. It does not justify a WoW punch constant or a blanket multiplier. The implementation is opt-in through the native amount adapters; the separate WoW compatibility formula is not changed here.

The verified profile applies to the five active native player-build identities and their generated party-NPC derived-stat snapshots: Knight/Scout, Priest/Knight, Rogue/Warrior, Scout/Rogue, and Mage/Warlock. It retains the existing training-profile residualFlatDamage bridge and tracked generic physical-damage multiplier. The native stat model does not represent all weapon-category damage/speed rates, so the profile records a neutral-rate boundary rather than claiming exactness for those channels.

Primary local source evidence

Paths below are relative to F:\Projects\Runewaker. Hashes are SHA-256 of the audited local files.

Source SHA-256 Relevant evidence
SourceCodeVS2019/Libraries/Union/RoleData/RoleAttr.cpp 8143A90CE826BF3152551A4508D212F186012CBC054E505E426551CB62369C1F Init_BaseEQValue lines 178–203 initializes unarmed main hand, speed 30, and main DMG/speed factors 1. _Main_ATKSpeed lines 533–580 applies generic AtkSpeed, main-hand speed factor, all-weapon speed rate, unarmed speed rate, and the weapon's base speed, clamped to 5. _MainDMG lines 713–758 applies main-hand DMG factor, all-weapon DMG rate, DMG_Per, and unarmed DMG rate; if BaseEQ.Main_DMG == 0, it assigns Level * 2, then returns max(1, (Fin.DMG + BaseEQ.Main_DMG) * DmgRate). AddAbility line 1323 routes type 25 into BaseEQ.Main_DMG; lines 1327–1374 route typed main damage and speed rates by weapon category. MainAtkDamage line 963 applies source weapon variance after Fin.Main_DMG.
SourceCodeVS2019/Libraries/Union/RoleData/ObjectStruct.h 708F2D087F2654B97D149AB7124D9CDFFC915CF8BF18108FEFF3F1A45F124C20 Defines EM_Weapon_Unarmed (line 1047) and the typed body-rate fields consumed by RoleAttr.
SourceCodeVS2019/Libraries/Union/RoleData/RoleBaseDef.h BAE1014F3628B4AD05C2E0F08CDCAE14D5F13A0981DAD4D592F2F0A10BFAAB18 _BASE_ATTACK_SPEED_ is 40; that default is not the unarmed main-hand value, which RoleAttr sets explicitly to 30.
SourceCodeVS2019/Server/NetGameServer/MainProc/Global_SimpleSchedular.cpp 5DCDBDD69EEFE1086E2087AB167270D580884929A083E4E69032C773F7F941AC Main-hand auto scheduling adds int(Fin.Main_ATKSpeed) to the server clock (line 445), so this source speed unit is milliseconds. The source auto-hit then calls MainAtkDamage (line 633).
SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp B2EEE402FDF9AC488ABE62DC8D2EC9C52B312542EF06D95A6B6FF594711CA7C3 Physics damage uses Main_DMG * 10 * SpellTime / Main_ATKSpeed (line 2327). Physics_Point subtracts Main_DMG * Dmg_MagArg once for fixed effects or times SpellTime otherwise (lines 2384–2386). These are native ability branches that must see the unarmed main-hand inputs after Disarm.
SourceCodeVS2019/Libraries/Union/RoleData/RD_Normal.cxx 5C0C726F469A7BE63D76114015BF169193A88F545209D4A4DEB5E090443C2692 CalAddDotMagicLv lines 2423 onward applies rank correction; its negative physical DotMagic branch reads Fin.Main_DMG and Fin.Main_ATKSpeed (line 2507).
SourceCodeVS2019/Libraries/Union/RoleData/RD_ValueCal.cxx D7F00AD9E760EC2263F81F231113F165D0D9F29EB2C329593C66F4C6288A69A9 Confirms the native Main_DMG accessor and unarmed category logic in the value-calculation path (lines 2529, 3642–3646).

The active ability bindings are src/game/generated/romNativeHolyHealBindings.generated.json (SHA-256 CE2F39D602296686382CDC424F9FEA4EFA8DE384093443311E9C8FB6C97AAA04) and src/game/generated/romNativePeriodicDamageBindings.generated.json (SHA-256 C01E17C4DF2ADB3B3A196D9EE1F84EA752BE94DBA81FB47E23A889599CB63D33). Their source metadata pins magiccollectobject.db (4aae19865cb07b30f99945aa7319a0096db244ff5e5b4b5d902fc6889474f501) and magicobject.db (52af13a2d8cfa9a66d4caa2d3c38832edc232f058995a64ef8135a6b82e43691) for player attack/periodic rows. The native stat-rule artifact pins racetable.db, sextable.db, voctable.db, and fixtable.db; romNativeStatRules.generated.json SHA-256 is 6DA531D6D56642013155438B93BB55C120A6421C7C12858CAD8C1B934E1E4E99.

The training item curve used for the bridge is src/game/generated/romNativeDamageTrainingProfiles.generated.json (SHA-256 A3E064F536F4E1EFAFA076D13755DE5DC62C23B20A496AE572F783126DBE3BCB). It records romHeirloomReference.json SHA-256 4E5194A28C2764838C5862C1F3DB505F5BE3CE99143A4440BAECF5E38C3099BF and the source item-database hashes: armorobject.db A2F0017C4772165D562EBB08CA0AC51625CDC4453E3670A1501DF7D741D2BAD3, weaponobject.db 2BC28EEBBE413BD6118F027E3FEC1A3953CEF371476937D0B65622C542971A67, addpowerobject.db D651861FC5918BAC937D726D754D17A92DA34DB5059F9C70AC9F6B2C15711432, and string_enus.db 4FCE021BA9ED110A213E08D3228B2DE57CEC66DDBBC8DCF5753204874D9AB75. These are the pins recorded by the generated reference, not a claim that the separate active Runewaker/Resource/data directory has those same hashes.

Implemented formula and adapter contract

For a verified active-five native training profile, the source-shaped unarmed main-hand amount is:

Main_DMG_unarmed = max(1, (residualFlatDamage + Level * 2) * physicalDamageRateFactor)
Main_ATKSpeed_unarmed = 30
Main_attack_interval = 30 * 100 ms = 3000 ms

residualFlatDamage is the existing native training-weapon bridge remainder after the generated profile's weapon contribution is removed. The physicalDamageRateFactor remains applied. Main-hand-only rate channels that this stats bridge cannot supply are held neutral and explicitly surfaced as unmodeledRateChannels: Main_DMG_Per, DMG_AllWeapon, DMG_Unarmed, AtkSpeed, Main_ATKSpeed_Per, AttackSpeed_AllWeapon, and AttackSpeed_Unarmed.

nativeDisarmedMainHandAutoAttackProfile(caster) only returns a profile when the current native derived stats match the canonical active-five training-gear profile. It identifies the main source as runewaker-roleattr-unarmed-main-hand, has no training weapon item IDs, and leaves physicalAttack intact. This profile is the prepared optional override for:

  • nativeDamageCasterAmount(..., targetContext?, mainHandOverride?): when the bound native component is calculation type 0 or 9, or has a nonzero fixed contribution of type 2, the adapter validates the override and feeds the lower native resolver a copied training profile whose main hand is Level * 2, speed 30, and sourceItemIds: []. The residual, generic rates, off-hand, and ranged entries are retained. Other components do not consume the substituted main-hand fields; a ranged branch therefore keeps its existing ranged inputs.
  • nativePeriodicDamageCasterSnapshot(..., magicResistance?, effectiveMagicPower?, mainHandOverride?): only the physical negative HP DotMagic rank-correction branch consumes the override. Other periodic branches keep their current source behavior.

In both APIs an omitted override preserves the pre-existing native calculation. A stale or invalid supplied override fails closed for a branch that consumes Main_DMG/Main_ATKSpeed (null for direct damage, undefined for a periodic snapshot). Custom/unverified gear returns no disarmed profile; callers must keep their existing explicit fallback behavior. This does not grant spell usability or certify native weapon/category prerequisites for every ability.

Known limits

  • This is the source main-hand input before MainAtkDamage variance, hit, target defense, crit, and other downstream combat resolution. It is not a complete auto-hit result.
  • The native rules sidecar models generic physical damage rate, not all seven unarmed/main/all-weapon rate channels listed above. Their neutral values are a boundary of this implementation, not a source claim that the rates are always 1.
  • The profile is verified against canonical generated training gear's tracked aggregate stats. A custom set whose tracked totals differ fails the gate, but a custom set with identical aggregates cannot be distinguished because the native gear payload has no item provenance. Equipment-specific BaseEQ.Main_DMG effects and unidentified weapon/category rate modifiers remain unmodeled.
  • Existing ability cast/equipment prerequisites remain unchanged and unverified by this amount adapter. Direct abilities continue through their existing shared status/availability checks.
  • The active-five generated party-NPC profile test establishes the stats preparation and formula input shape; it does not certify all classes, every NPC type, every ability, or native combat readiness.

Verification

Focused source tests passed:

npx vitest run src/game/romNativeDisarm.test.ts src/game/romNativeCombatAmount.integration.test.ts src/game/romNativePeriodicDamage.test.ts src/game/romNativeDamage.test.ts
4 files passed; 25 tests passed.

The Disarm tests cover all five active identities as player and configured party-NPC derived stats, preserve the residual and tracked multiplier, reject custom gear and stale override values, confirm native main-hand-consuming basic-attack branches change while the ranged Scout Shot remains unchanged, and verify a physical DotMagic rank correction responds to the unarmed inputs. This was a scoped source/runtime test run, not a full balance/readiness pass.