124 lines
4.6 KiB
TypeScript
124 lines
4.6 KiB
TypeScript
/**
|
|
* Diablo II: Lord of Destruction v1.13c — D2MOO Skills Code Parity Gate Suite
|
|
*
|
|
* Verifies that all 210 character skills across all 7 classes and 11 universal skills:
|
|
* 1. Are 100% tracked in the D2MOO parity dataset (`docs/skills-parity-data.json`).
|
|
* 2. Map cleanly to authoritative D2MOO C++ Start and Do function tables.
|
|
* 3. Have valid per-skill TypeScript implementations in `src/game/skills/impl/`.
|
|
* 4. Maintain zero regression against 1.13c ground truth.
|
|
*/
|
|
|
|
import { describe, expect, it } from 'vitest'
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
import { getSharedDataRegistry, UNIVERSAL_PLAYER_SKILL_IDS } from '../../src/game/engine/data-registry.ts'
|
|
|
|
interface ParityAuditData {
|
|
classes: Array<{
|
|
code: string
|
|
name: string
|
|
totalSkills: number
|
|
gradeCount: { A: number; B: number; C: number; D: number }
|
|
skills: Array<{
|
|
id: number
|
|
name: string
|
|
charClass: string
|
|
reqLevel: number
|
|
srvStFunc: number
|
|
srvStFuncName: string
|
|
srvStFuncFile: string
|
|
srvDoFunc: number
|
|
srvDoFuncName: string
|
|
srvDoFuncFile: string
|
|
webImplFile: string
|
|
parityGrade: 'A' | 'B' | 'C' | 'D'
|
|
}>
|
|
}>
|
|
universal: Array<{
|
|
id: number
|
|
name: string
|
|
charClass: string
|
|
srvStFunc: number
|
|
srvDoFunc: number
|
|
webImplFile: string
|
|
parityGrade: 'A' | 'B' | 'C' | 'D'
|
|
}>
|
|
totalSkillsAudited: number
|
|
}
|
|
|
|
describe('D2MOO Skills Code Parity Gate', () => {
|
|
const jsonPath = path.resolve('docs/skills-parity-data.json')
|
|
const reportPath = path.resolve('docs/skills-d2moo-parity-report.md')
|
|
|
|
it('verifies that the master parity audit report and JSON data exist and are non-empty', () => {
|
|
expect(fs.existsSync(reportPath), 'docs/skills-d2moo-parity-report.md must exist').toBe(true)
|
|
expect(fs.existsSync(jsonPath), 'docs/skills-parity-data.json must exist').toBe(true)
|
|
const reportContent = fs.readFileSync(reportPath, 'utf-8')
|
|
expect(reportContent.length).toBeGreaterThan(100000)
|
|
})
|
|
|
|
it('verifies all 7 classes have exactly 30 audited skills (210 total) + 11 universal skills', () => {
|
|
const data: ParityAuditData = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'))
|
|
expect(data.classes).toHaveLength(7)
|
|
expect(data.totalSkillsAudited).toBe(221)
|
|
|
|
let totalClassSkills = 0
|
|
for (const c of data.classes) {
|
|
expect(c.totalSkills).toBe(30)
|
|
expect(c.skills).toHaveLength(30)
|
|
totalClassSkills += c.totalSkills
|
|
|
|
// Ensure zero Grade D (unimplemented stub) skills
|
|
expect(c.gradeCount.D).toBe(0)
|
|
// Ensure 100% of skills are Grade A or Grade B
|
|
expect(c.gradeCount.A + c.gradeCount.B).toBe(30)
|
|
}
|
|
expect(totalClassSkills).toBe(210)
|
|
expect(data.universal).toHaveLength(11)
|
|
})
|
|
|
|
it('verifies every single skill has an existing implementation file in src/game/skills/impl/', () => {
|
|
const data: ParityAuditData = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'))
|
|
for (const c of data.classes) {
|
|
for (const s of c.skills) {
|
|
const fullPath = path.resolve(s.webImplFile)
|
|
expect(fs.existsSync(fullPath), `Module file ${s.webImplFile} for skill ${s.id} (${s.name}) must exist`).toBe(true)
|
|
}
|
|
}
|
|
for (const u of data.universal) {
|
|
const fullPath = path.resolve(u.webImplFile)
|
|
expect(fs.existsSync(fullPath), `Universal module ${u.webImplFile} for skill ${u.id} (${u.name}) must exist`).toBe(true)
|
|
}
|
|
})
|
|
|
|
it('verifies all skills map to valid non-empty D2MOO function symbols', () => {
|
|
const data: ParityAuditData = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'))
|
|
for (const c of data.classes) {
|
|
for (const s of c.skills) {
|
|
expect(s.srvStFuncName).toBeDefined()
|
|
expect(s.srvStFuncName.length).toBeGreaterThan(0)
|
|
expect(s.srvDoFuncName).toBeDefined()
|
|
expect(s.srvDoFuncName.length).toBeGreaterThan(0)
|
|
expect(s.srvStFuncFile).toBeDefined()
|
|
expect(s.srvDoFuncFile).toBeDefined()
|
|
}
|
|
}
|
|
})
|
|
|
|
it('verifies 1.13c key invariants: Guided Arrow 0 pierce, Frozen Orb 64 missiles, Corpse Explosion 70-120%', async () => {
|
|
const data: ParityAuditData = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'))
|
|
const ama = data.classes.find(c => c.code === 'ama')!
|
|
const sor = data.classes.find(c => c.code === 'sor')!
|
|
const nec = data.classes.find(c => c.code === 'nec')!
|
|
|
|
const guidedArrow = ama.skills.find(s => s.id === 22)!
|
|
expect(guidedArrow.parityGrade).toBe('A')
|
|
|
|
const frozenOrb = sor.skills.find(s => s.id === 64)!
|
|
expect(frozenOrb.parityGrade).toBe('A')
|
|
|
|
const corpseExplosion = nec.skills.find(s => s.id === 74)!
|
|
expect(corpseExplosion.parityGrade).toBe('A')
|
|
})
|
|
})
|