Files
2026-09-08 10:40:26 -04:00

3.2 KiB

RuneWaker combat formula and loot fidelity

Combat damage

Imported RuneWaker damage mechanics now carry a runewakerFormula descriptor from magicobject.db into the generated enemy attack. The descriptor keeps the source spell/component ids, spell level, damage power, fixed value, damage-over-time base, calculation family, and source-code provenance.

The preserved implementation is in SourceCodeVS2019/Server/NetGameServer/MainProc/MagicProc/MagicProcess.cpp:

  • CalPhyDamage begins at line 3366.
  • CalMagicDamage begins at line 3651.
  • Both use the source ATK/DEF or MATK/MDEF ratio max(0.2, 1 + ((attack + 100) - (defense + 200)) / max(attack + 100, defense + 200)).
  • Both also depend on native weapon or magic damage, level-difference tables, resistance processing, critical power, fixed-attribute lookup, and global NPC/PvP/AoE/target rates.

HealerMan does not have equivalent RuneWaker ATK, MATK, DEF, MDEF, FixType, native weapon/magic damage, level-rate tables, or global rate arrays. The adapter therefore does not label its live result as exact RuneWaker damage:

  • Negative Atk.DmgPower values with magnitude at most 4 are treated as source ratios against HealerMan's level-scaled enemy damage budget; the native 0.2 minimum is retained.
  • Point-valued source powers use the explicit runtimeBaselineMultiplier balancing adapter stored with the formula.
  • HealerMan's existing hit table, armor, resistance, critical, aura, and absorb pipeline runs afterward and remains identified as HealerMan/WotLK-shaped mitigation.
  • Unclassified formula records return zero from the adapter rather than executing guessed damage.

resolveRunewakerNativeResolvedPower exposes the source-shaped tail for later use. It requires every native-only input as an argument and supplies no silent one-valued defaults.

Loot

export-loot-catalog.mjs reads each source-classified boss's 15 independent NPCObjectDB.DropItem slots. It preserves the original rate denominator of 100,000 and recursively expands TreasureObjectDB's weighted, mutually exclusive outcomes and fixed DropCount quantities.

Runtime rules are intentionally bounded:

  • Recognized leaves come only from itemobject.db, armorobject.db, weaponobject.db, cardobject.db, runeobject.db, recipeobject.db, and keyitemobject.db.
  • Treasure recursion stops at 12 levels and cycles fail closed.
  • Unknown object ids fail closed.
  • Treasure tables with a Lua predicate fail closed because executing arbitrary preserved server Lua is outside the browser runtime.
  • NeedDLv requirements are preserved and evaluated from the caller-provided source level delta.
  • Source names are retained, but source stats, quality, and equipment slots are not inferred. Portable drops enter inventory as common, non-equipable misc items with zero stats.

The detailed status for every boss is generated in RUNEWAKER_LOOT_IMPORT_REPORT.md. The full probability graph is available in public/data/runewaker-loot-catalog.json and the runtime snapshot.

Refresh and verification

npm run runewaker:spells:refresh
npm run runewaker:loot:refresh
node --test scripts/runewaker-pipeline/runewaker-loot-import.test.mjs
vitest run src/game/runewakerCombatFormula.test.ts src/game/runewakerLootCatalog.test.ts