46 lines
3.2 KiB
Markdown
46 lines
3.2 KiB
Markdown
# 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
|
|
```
|