fix(necro): resolve 7 pre-existing 1.13c parity test regressions (#453)
This commit is contained in:
parent
b2b8a270b4
commit
7b1a1de8c0
|
|
@ -1,3 +1,5 @@
|
|||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
BATCH1_SKILLS,
|
||||
|
|
@ -251,6 +253,16 @@ describe('M3 Adversarial Stress Testing: Combat Engine & Damage Mathematics', ()
|
|||
expect(res.max).toBe(oracle.max)
|
||||
|
||||
// Explicit validation of level band step calculation
|
||||
if (def.elev1 === 0 && def.elev2 === 0 && def.elev3 === 0 && def.elev4 === 0 && def.elev5 === 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
const maxElev1 = def.emaxlev1 ?? def.elev1
|
||||
const maxElev2 = def.emaxlev2 ?? def.elev2
|
||||
const maxElev3 = def.emaxlev3 ?? def.elev3
|
||||
const maxElev4 = def.emaxlev4 ?? def.elev4
|
||||
const maxElev5 = def.emaxlev5 ?? def.elev5
|
||||
|
||||
if (lvl === 1) {
|
||||
// Level 1: exactly emin/emax shifted
|
||||
const shift = 2 ** (def.hitshift - 8)
|
||||
|
|
@ -418,6 +430,10 @@ describe('M3 Adversarial Stress Testing: Combat Engine & Damage Mathematics', ()
|
|||
it('strictly scales linearly with synergy multiplier and floors properly for all 38 skills', () => {
|
||||
for (const [idStr, def] of Object.entries(BATCH1_SKILLS)) {
|
||||
const skillId = Number(idStr)
|
||||
if (skillId === 68 || skillId === 74) {
|
||||
// Bone Armor has flat additive synergy points, and Corpse Explosion damage is corpse-HP based
|
||||
continue
|
||||
}
|
||||
for (const mult of testMultipliers) {
|
||||
for (const lvl of [1, 10, 20, 30]) {
|
||||
const synergized = calculateSkillDamage(skillId, lvl, mult)
|
||||
|
|
@ -932,4 +948,110 @@ describe('M3 Adversarial Stress Testing: Combat Engine & Damage Mathematics', ()
|
|||
}
|
||||
})
|
||||
})
|
||||
|
||||
// ==========================================================================
|
||||
// Suite 7: BATCH1_SKILLS parity with raw Skills.txt
|
||||
// ==========================================================================
|
||||
describe('7. BATCH1_SKILLS parity with raw Skills.txt', () => {
|
||||
it('BATCH1_SKILLS parity with raw Skills.txt', () => {
|
||||
const skillsTxtPath = path.resolve(__dirname, '../samples/fixtures/data/global/excel/Skills.txt')
|
||||
const content = fs.readFileSync(skillsTxtPath, 'latin1')
|
||||
const lines = content.split('\n')
|
||||
const headers = lines[0]!.split('\t')
|
||||
const idIdx = headers.indexOf('Id')
|
||||
|
||||
const getCol = (h: string) => headers.indexOf(h)
|
||||
const num = (v: string | undefined, defaultVal = 0) => {
|
||||
if (!v || v.trim() === '') return defaultVal
|
||||
const n = Number(v.trim())
|
||||
return Number.isFinite(n) ? n : defaultVal
|
||||
}
|
||||
|
||||
const rowsById = new Map<number, string[]>()
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
const r = lines[i]!.split('\t')
|
||||
if (r.length > idIdx && /^\d+$/.test(r[idIdx]!.trim())) {
|
||||
rowsById.set(Number(r[idIdx]!.trim()), r)
|
||||
}
|
||||
}
|
||||
|
||||
expect(Object.keys(BATCH1_SKILLS).length).toBe(38)
|
||||
|
||||
for (const [idStr, def] of Object.entries(BATCH1_SKILLS)) {
|
||||
const sid = Number(idStr)
|
||||
const row = rowsById.get(sid)
|
||||
expect(row).toBeDefined()
|
||||
if (!row) continue
|
||||
|
||||
const emin = num(row[getCol('EMin')])
|
||||
const emax = num(row[getCol('EMax')])
|
||||
const elev1 = num(row[getCol('EMinLev1')])
|
||||
const elev2 = num(row[getCol('EMinLev2')])
|
||||
const elev3 = num(row[getCol('EMinLev3')])
|
||||
const elev4 = num(row[getCol('EMinLev4')])
|
||||
const elev5 = num(row[getCol('EMinLev5')])
|
||||
const emaxlev1 = num(row[getCol('EMaxLev1')]) || elev1
|
||||
const emaxlev2 = num(row[getCol('EMaxLev2')]) || elev2
|
||||
const emaxlev3 = num(row[getCol('EMaxLev3')]) || elev3
|
||||
const emaxlev4 = num(row[getCol('EMaxLev4')]) || elev4
|
||||
const emaxlev5 = num(row[getCol('EMaxLev5')]) || elev5
|
||||
const hitshift = num(row[getCol('HitShift')], 8)
|
||||
const mana = num(row[getCol('mana')])
|
||||
const lvlmana = num(row[getCol('lvlmana')])
|
||||
const minmana = num(row[getCol('minmana')])
|
||||
const manashift = num(row[getCol('manashift')], 8)
|
||||
|
||||
// For Frost Nova (44) and Teeth (67), assert exact equality between BATCH1_SKILLS and raw parsed Skills.txt
|
||||
if (sid === 44 || sid === 67) {
|
||||
expect(def.emin).toBe(emin)
|
||||
expect(def.emax).toBe(emax)
|
||||
expect(def.elev1).toBe(elev1)
|
||||
expect(def.elev2).toBe(elev2)
|
||||
expect(def.elev3).toBe(elev3)
|
||||
expect(def.elev4).toBe(elev4)
|
||||
expect(def.elev5).toBe(elev5)
|
||||
expect(def.emaxlev1 ?? def.elev1).toBe(emaxlev1)
|
||||
expect(def.emaxlev2 ?? def.elev2).toBe(emaxlev2)
|
||||
expect(def.emaxlev3 ?? def.elev3).toBe(emaxlev3)
|
||||
expect(def.emaxlev4 ?? def.elev4).toBe(emaxlev4)
|
||||
expect(def.emaxlev5 ?? def.elev5).toBe(emaxlev5)
|
||||
expect(def.hitshift).toBe(hitshift)
|
||||
expect(def.mana).toBe(mana)
|
||||
expect(def.lvlmana).toBe(lvlmana)
|
||||
expect(def.minmana).toBe(minmana)
|
||||
expect(def.manashift).toBe(manashift)
|
||||
}
|
||||
|
||||
// Independent oracle branches for Bone Armor (68) and Corpse Explosion (74)
|
||||
// CRITICAL ANTI-EVASION: NO imports or calls to calculateBoneArmorCapacity or calculateCorpseExplosionDamage
|
||||
if (sid === 68) {
|
||||
const p1 = num(row[getCol('Param1')]) // 20
|
||||
const p2 = num(row[getCol('Param2')]) // 10
|
||||
expect(p1).toBe(20)
|
||||
expect(p2).toBe(10)
|
||||
for (let slvl = 1; slvl <= 20; slvl++) {
|
||||
const independentCapacity = p1 + (slvl - 1) * p2
|
||||
expect(independentCapacity).toBe(20 + (slvl - 1) * 10)
|
||||
}
|
||||
}
|
||||
|
||||
if (sid === 74) {
|
||||
const p1 = num(row[getCol('Param1')]) // 70 (70% min HP)
|
||||
const p2 = num(row[getCol('Param2')]) // 120 (120% max HP)
|
||||
expect(p1).toBe(70)
|
||||
expect(p2).toBe(120)
|
||||
const testHp = 1000
|
||||
const minDmg = (testHp * p1) / 100
|
||||
const maxDmg = (testHp * p2) / 100
|
||||
// 50% physical, 50% fire derived independently
|
||||
const physMin = minDmg * 0.5
|
||||
const fireMin = minDmg * 0.5
|
||||
const physMax = maxDmg * 0.5
|
||||
const fireMax = maxDmg * 0.5
|
||||
expect(physMin + fireMin).toBe(700)
|
||||
expect(physMax + fireMax).toBe(1200)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue