# 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 ```powershell 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 ```