536 lines
18 KiB
TypeScript
536 lines
18 KiB
TypeScript
import { describe, it, expect, beforeAll } from 'vitest'
|
|
import * as fs from 'fs'
|
|
import { MountedArchives } from '../src/mpq/mount.ts'
|
|
import { MpqArchive } from '../src/mpq/archive.ts'
|
|
import { fileSource } from '../src/mpq/file-source.ts'
|
|
import type { D2Table } from '../src/game/acts.ts'
|
|
import { parseTable, cell } from '../src/game/acts.ts'
|
|
import {
|
|
parseGemsTable,
|
|
loadGems,
|
|
getGemModifiers,
|
|
getMaxSockets,
|
|
parseParam,
|
|
parseNumber,
|
|
GEMS_TABLE_PATH,
|
|
type GemTable,
|
|
type Gem,
|
|
} from '../src/game/gems.ts'
|
|
import {
|
|
loadItemTypes,
|
|
isA,
|
|
type ItemTypeTable,
|
|
type ItemTypeDefinition,
|
|
} from '../src/game/item-types.ts'
|
|
|
|
const hasD2 = fs.existsSync('samples/d2/d2data.mpq') && fs.existsSync('samples/d2/Patch_D2.mpq')
|
|
|
|
/**
|
|
* Minimal mock D2Table containing Chipped Amethyst, Expansion row, and El Rune.
|
|
*/
|
|
function createMinimalMockGemsTable(): D2Table {
|
|
const header = [
|
|
'name', 'letter', 'transform', 'code', 'nummods',
|
|
'weaponMod1Code', 'weaponMod1Param', 'weaponMod1Min', 'weaponMod1Max',
|
|
'weaponMod2Code', 'weaponMod2Param', 'weaponMod2Min', 'weaponMod2Max',
|
|
'weaponMod3Code', 'weaponMod3Param', 'weaponMod3Min', 'weaponMod3Max',
|
|
'helmMod1Code', 'helmMod1Param', 'helmMod1Min', 'helmMod1Max',
|
|
'helmMod2Code', 'helmMod2Param', 'helmMod2Min', 'helmMod2Max',
|
|
'helmMod3Code', 'helmMod3Param', 'helmMod3Min', 'helmMod3Max',
|
|
'shieldMod1Code', 'shieldMod1Param', 'shieldMod1Min', 'shieldMod1Max',
|
|
'shieldMod2Code', 'shieldMod2Param', 'shieldMod2Min', 'shieldMod2Max',
|
|
'shieldMod3Code', 'shieldMod3Param', 'shieldMod3Min', 'shieldMod3Max',
|
|
]
|
|
|
|
const rows: string[][] = [
|
|
// 0: Chipped Amethyst (gcv)
|
|
[
|
|
'Chipped Amethyst', '', '18', 'gcv', '3',
|
|
'att', '0', '40', '40',
|
|
'', '', '', '',
|
|
'', '0', '0', '0',
|
|
'str', '0', '3', '3',
|
|
'', '0', '0', '0',
|
|
'', '0', '0', '0',
|
|
'ac', '0', '8', '8',
|
|
'', '0', '0', '0',
|
|
'', '0', '0', '0',
|
|
],
|
|
// 1: Expansion row (separator to be filtered out)
|
|
[
|
|
'Expansion', '', '', '', '',
|
|
'', '', '', '',
|
|
'', '', '', '',
|
|
'', '', '', '',
|
|
'', '', '', '',
|
|
'', '', '', '',
|
|
'', '', '', '',
|
|
'', '', '', '',
|
|
'', '', '', '',
|
|
'', '', '', '',
|
|
],
|
|
// 2: El Rune (r01)
|
|
[
|
|
'El Rune', 'El', '18', 'r01', '1',
|
|
'light', '', '1', '1',
|
|
'att', '', '50', '50',
|
|
'', '', '', '',
|
|
'light', '', '1', '1',
|
|
'ac', '', '15', '15',
|
|
'', '', '', '',
|
|
'light', '', '1', '1',
|
|
'ac', '', '15', '15',
|
|
'', '', '', '',
|
|
],
|
|
]
|
|
|
|
return { header, rows }
|
|
}
|
|
|
|
describe('Gems and Runes Socketing (Issue #106)', () => {
|
|
describe('Helper functions: parseParam & parseNumber', () => {
|
|
it('parses empty and whitespace strings as undefined in parseParam', () => {
|
|
expect(parseParam(undefined)).toBeUndefined()
|
|
expect(parseParam('')).toBeUndefined()
|
|
expect(parseParam(' ')).toBeUndefined()
|
|
expect(parseParam('\t')).toBeUndefined()
|
|
})
|
|
|
|
it('parses numeric strings as numbers in parseParam', () => {
|
|
expect(parseParam('0')).toBe(0)
|
|
expect(parseParam('125')).toBe(125)
|
|
expect(parseParam('-20')).toBe(-20)
|
|
expect(typeof parseParam('125')).toBe('number')
|
|
})
|
|
|
|
it('preserves non-numeric strings in parseParam', () => {
|
|
expect(parseParam('Oak Sage')).toBe('Oak Sage')
|
|
expect(parseParam('cold')).toBe('cold')
|
|
})
|
|
|
|
it('parses numbers with fallback in parseNumber', () => {
|
|
expect(parseNumber(undefined)).toBe(0)
|
|
expect(parseNumber('')).toBe(0)
|
|
expect(parseNumber('', 10)).toBe(10)
|
|
expect(parseNumber('40')).toBe(40)
|
|
expect(parseNumber('-15')).toBe(-15)
|
|
expect(parseNumber('invalid', 99)).toBe(99)
|
|
})
|
|
})
|
|
|
|
describe('Standalone Table Parsing', () => {
|
|
it('parses D2Table and filters Expansion row', () => {
|
|
const mockTable = createMinimalMockGemsTable()
|
|
const gemTable = parseGemsTable(mockTable)
|
|
|
|
expect(gemTable.length).toBe(2)
|
|
expect(gemTable.all.length).toBe(2)
|
|
|
|
const gcv = gemTable.get('gcv')
|
|
expect(gcv).toBeDefined()
|
|
expect(gcv?.name).toBe('Chipped Amethyst')
|
|
expect(gcv?.isGem).toBe(true)
|
|
expect(gcv?.isRune).toBe(false)
|
|
expect(gcv?.transform).toBe(18)
|
|
expect(gcv?.nummods).toBe(3)
|
|
|
|
expect(gcv?.weaponMods).toHaveLength(1)
|
|
expect(gcv?.weaponMods[0]).toEqual({ code: 'att', param: 0, par: 0, min: 40, max: 40 })
|
|
|
|
expect(gcv?.helmMods).toHaveLength(1)
|
|
expect(gcv?.helmMods[0]).toEqual({ code: 'str', param: 0, par: 0, min: 3, max: 3 })
|
|
|
|
expect(gcv?.shieldMods).toHaveLength(1)
|
|
expect(gcv?.shieldMods[0]).toEqual({ code: 'ac', param: 0, par: 0, min: 8, max: 8 })
|
|
|
|
const r01 = gemTable.get('r01')
|
|
expect(r01).toBeDefined()
|
|
expect(r01?.name).toBe('El Rune')
|
|
expect(r01?.letter).toBe('El')
|
|
expect(r01?.isGem).toBe(false)
|
|
expect(r01?.isRune).toBe(true)
|
|
expect(r01?.weaponMods).toHaveLength(2)
|
|
})
|
|
|
|
it('supports TSV string and Uint8Array parsing', () => {
|
|
const tsv = [
|
|
'name\tletter\ttransform\tcode\tnummods\tweaponMod1Code\tweaponMod1Param\tweaponMod1Min\tweaponMod1Max\thelmMod1Code\thelmMod1Param\thelmMod1Min\thelmMod1Max\tshieldMod1Code\tshieldMod1Param\tshieldMod1Min\tshieldMod1Max',
|
|
'Chipped Ruby\t\t1\tgcr\t3\tfire-min\t0\t3\t3\thp\t0\t10\t10\tres-fire\t0\t12\t12',
|
|
].join('\n')
|
|
|
|
const fromString = parseGemsTable(tsv)
|
|
expect(fromString.length).toBe(1)
|
|
expect(fromString.get('gcr')?.name).toBe('Chipped Ruby')
|
|
|
|
const fromBytes = parseGemsTable(new TextEncoder().encode(tsv))
|
|
expect(fromBytes.length).toBe(1)
|
|
expect(fromBytes.get('gcr')?.name).toBe('Chipped Ruby')
|
|
})
|
|
|
|
it('supports case-insensitive code and name lookup, iteration, and indexing', () => {
|
|
const mockTable = createMinimalMockGemsTable()
|
|
const table = parseGemsTable(mockTable)
|
|
|
|
expect(table.get('GCV')?.name).toBe('Chipped Amethyst')
|
|
expect(table.get('chipped amethyst')?.code).toBe('gcv')
|
|
expect(table.byCode.get('R01')?.name).toBe('El Rune')
|
|
expect(table.byName.get('EL RUNE')?.code).toBe('r01')
|
|
|
|
const codes = [...table].map(g => g.code)
|
|
expect(codes).toEqual(['gcv', 'r01'])
|
|
|
|
expect(table[0]?.code).toBe('gcv')
|
|
expect(table[1]?.code).toBe('r01')
|
|
expect(table[2]).toBeUndefined()
|
|
})
|
|
})
|
|
|
|
describe('Socket Modifier Dispatch (getGemModifiers)', () => {
|
|
const mockTable = createMinimalMockGemsTable()
|
|
const gemTable = parseGemsTable(mockTable)
|
|
|
|
const mockIsA = (child: string, parent: string): boolean => {
|
|
if (child === 'swor' && parent === 'weap') return true
|
|
if (child === 'axe' && parent === 'weap') return true
|
|
if (child === 'phlm' && parent === 'helm') return true
|
|
if (child === 'circ' && parent === 'helm') return true
|
|
if (child === 'ashd' && parent === 'shld') return true
|
|
if (child === 'head' && parent === 'shld') return true
|
|
return false
|
|
}
|
|
|
|
it('dispatches weaponMod1..3 for weapons', () => {
|
|
const sworMods = getGemModifiers('gcv', 'swor', mockIsA, gemTable)
|
|
expect(sworMods).toHaveLength(1)
|
|
expect(sworMods[0]?.code).toBe('att')
|
|
expect(sworMods[0]?.min).toBe(40)
|
|
expect(sworMods[0]?.max).toBe(40)
|
|
|
|
const swordLiteralMods = getGemModifiers('gcv', 'sword', mockIsA, gemTable)
|
|
expect(swordLiteralMods).toEqual(sworMods)
|
|
|
|
const axeMods = getGemModifiers('gcv', 'axe', mockIsA, gemTable)
|
|
expect(axeMods).toEqual(sworMods)
|
|
})
|
|
|
|
it('dispatches helmMod1..3 for helmets AND body armor (tors)', () => {
|
|
// Direct helm
|
|
const helmMods = getGemModifiers('gcv', 'helm', mockIsA, gemTable)
|
|
expect(helmMods).toHaveLength(1)
|
|
expect(helmMods[0]?.code).toBe('str')
|
|
expect(helmMods[0]?.min).toBe(3)
|
|
expect(helmMods[0]?.max).toBe(3)
|
|
|
|
// Primal helm (barbarian)
|
|
const phlmMods = getGemModifiers('gcv', 'phlm', mockIsA, gemTable)
|
|
expect(phlmMods).toEqual(helmMods)
|
|
|
|
// CRITICAL: Body armor ('tors') uses helm modifiers!
|
|
const torsoMods = getGemModifiers('gcv', 'tors', mockIsA, gemTable)
|
|
expect(torsoMods).toHaveLength(1)
|
|
expect(torsoMods[0]?.code).toBe('str')
|
|
expect(torsoMods[0]?.min).toBe(3)
|
|
expect(torsoMods[0]?.max).toBe(3)
|
|
|
|
// Literal 'torso'
|
|
const torsoLiteralMods = getGemModifiers('gcv', 'torso', mockIsA, gemTable)
|
|
expect(torsoLiteralMods).toEqual(helmMods)
|
|
})
|
|
|
|
it('dispatches shieldMod1..3 for shields', () => {
|
|
const shieldMods = getGemModifiers('gcv', 'shie', mockIsA, gemTable)
|
|
expect(shieldMods).toHaveLength(1)
|
|
expect(shieldMods[0]?.code).toBe('ac')
|
|
expect(shieldMods[0]?.min).toBe(8)
|
|
expect(shieldMods[0]?.max).toBe(8)
|
|
|
|
const shldMods = getGemModifiers('gcv', 'shld', mockIsA, gemTable)
|
|
expect(shldMods).toEqual(shieldMods)
|
|
|
|
const ashdMods = getGemModifiers('gcv', 'ashd', mockIsA, gemTable)
|
|
expect(ashdMods).toEqual(shieldMods)
|
|
|
|
const headMods = getGemModifiers('gcv', 'head', mockIsA, gemTable)
|
|
expect(headMods).toEqual(shieldMods)
|
|
})
|
|
|
|
it('verifies Chipped Amethyst (gcv): att 40 on sword, str 3 on helm, str 3 on torso, ac 8 on shield', () => {
|
|
const sword = getGemModifiers('gcv', 'swor', mockIsA, gemTable)
|
|
expect(sword).toEqual([
|
|
expect.objectContaining({ code: 'att', min: 40, max: 40 }),
|
|
])
|
|
|
|
const helm = getGemModifiers('gcv', 'helm', mockIsA, gemTable)
|
|
expect(helm).toEqual([
|
|
expect.objectContaining({ code: 'str', min: 3, max: 3 }),
|
|
])
|
|
|
|
const torso = getGemModifiers('gcv', 'tors', mockIsA, gemTable)
|
|
expect(torso).toEqual([
|
|
expect.objectContaining({ code: 'str', min: 3, max: 3 }),
|
|
])
|
|
|
|
const shield = getGemModifiers('gcv', 'shie', mockIsA, gemTable)
|
|
expect(shield).toEqual([
|
|
expect.objectContaining({ code: 'ac', min: 8, max: 8 }),
|
|
])
|
|
})
|
|
|
|
it('returns empty array for non-socketable item types or unknown gems', () => {
|
|
expect(getGemModifiers('gcv', 'boot', mockIsA, gemTable)).toEqual([])
|
|
expect(getGemModifiers('gcv', 'glov', mockIsA, gemTable)).toEqual([])
|
|
expect(getGemModifiers('gcv', 'belt', mockIsA, gemTable)).toEqual([])
|
|
expect(getGemModifiers('gcv', 'ring', mockIsA, gemTable)).toEqual([])
|
|
expect(getGemModifiers('unknown', 'swor', mockIsA, gemTable)).toEqual([])
|
|
})
|
|
})
|
|
|
|
describe('Max Sockets Calculation (getMaxSockets)', () => {
|
|
const swordType: ItemTypeDefinition = {
|
|
code: 'swor',
|
|
name: 'Sword',
|
|
equiv1: 'weap',
|
|
equiv2: '',
|
|
treasureClass: 0,
|
|
rarity: 0,
|
|
maxSock1: 3,
|
|
maxSock25: 4,
|
|
maxSock40: 6,
|
|
magic: false,
|
|
rare: false,
|
|
normal: false,
|
|
charm: false,
|
|
gem: false,
|
|
staffMods: '',
|
|
body: true,
|
|
bodyLoc1: 'rarm',
|
|
bodyLoc2: 'larm',
|
|
beltable: false,
|
|
class: '',
|
|
}
|
|
|
|
const helmType: ItemTypeDefinition = {
|
|
code: 'helm',
|
|
name: 'Helm',
|
|
equiv1: 'armo',
|
|
equiv2: '',
|
|
treasureClass: 0,
|
|
rarity: 0,
|
|
maxSock1: 2,
|
|
maxSock25: 2,
|
|
maxSock40: 3,
|
|
magic: false,
|
|
rare: false,
|
|
normal: false,
|
|
charm: false,
|
|
gem: false,
|
|
staffMods: '',
|
|
body: true,
|
|
bodyLoc1: 'head',
|
|
bodyLoc2: '',
|
|
beltable: false,
|
|
class: '',
|
|
}
|
|
|
|
it('calculates max sockets correctly across ilvl tiers (ilvl 24, 25, 39, 40)', () => {
|
|
// Crystal Sword (base gemsockets = 6)
|
|
expect(getMaxSockets(6, swordType, 1)).toBe(3)
|
|
expect(getMaxSockets(6, swordType, 24)).toBe(3)
|
|
expect(getMaxSockets(6, swordType, 25)).toBe(4)
|
|
expect(getMaxSockets(6, swordType, 39)).toBe(4)
|
|
expect(getMaxSockets(6, swordType, 40)).toBe(6)
|
|
expect(getMaxSockets(6, swordType, 85)).toBe(6)
|
|
|
|
// Crown (base gemsockets = 3)
|
|
expect(getMaxSockets(3, helmType, 1)).toBe(2)
|
|
expect(getMaxSockets(3, helmType, 24)).toBe(2)
|
|
expect(getMaxSockets(3, helmType, 25)).toBe(2)
|
|
expect(getMaxSockets(3, helmType, 39)).toBe(2)
|
|
expect(getMaxSockets(3, helmType, 40)).toBe(3)
|
|
})
|
|
|
|
it('correctly caps allowed sockets by base item gemsockets', () => {
|
|
// Short Sword (base gemsockets = 2)
|
|
expect(getMaxSockets(2, swordType, 24)).toBe(2)
|
|
expect(getMaxSockets(2, swordType, 25)).toBe(2)
|
|
expect(getMaxSockets(2, swordType, 40)).toBe(2)
|
|
|
|
// Cap (base gemsockets = 2, helm maxSock40 = 3)
|
|
expect(getMaxSockets(2, helmType, 40)).toBe(2)
|
|
})
|
|
|
|
it('returns 0 for base gemsockets <= 0', () => {
|
|
expect(getMaxSockets(0, swordType, 50)).toBe(0)
|
|
expect(getMaxSockets(-1, swordType, 50)).toBe(0)
|
|
})
|
|
})
|
|
|
|
describe('Integration with samples/d2 MPQs', () => {
|
|
let rawTable: D2Table
|
|
let gemTable: GemTable
|
|
let itemTypes: ItemTypeTable
|
|
|
|
beforeAll(async () => {
|
|
if (!hasD2) return
|
|
|
|
const archives = new MountedArchives()
|
|
for (const name of ['d2data.mpq', 'd2exp.mpq', 'Patch_D2.mpq']) {
|
|
archives.add(name, await MpqArchive.open(await fileSource(`samples/d2/${name}`)))
|
|
}
|
|
|
|
const bytes = await archives.read(GEMS_TABLE_PATH)
|
|
rawTable = parseTable(bytes)
|
|
gemTable = await loadGems(archives)
|
|
itemTypes = await loadItemTypes(archives)
|
|
})
|
|
|
|
it('verifies GEMS_TABLE_PATH is data\\global\\excel\\Gems.txt', () => {
|
|
expect(GEMS_TABLE_PATH).toBe('data\\global\\excel\\Gems.txt')
|
|
})
|
|
|
|
it('parses exactly 68 rows from 69 raw rows in 1.13c Gems.txt', () => {
|
|
if (!hasD2) return
|
|
|
|
expect(rawTable.rows.length).toBe(69)
|
|
expect(rawTable.header.length).toBe(41)
|
|
|
|
// Expansion separator row at index 35
|
|
const expansionRow = rawTable.rows[35]!
|
|
expect(cell(rawTable, expansionRow, 'name')).toBe('Expansion')
|
|
expect(cell(rawTable, expansionRow, 'code')).toBe('')
|
|
|
|
// Exactly 68 valid rows parsed
|
|
expect(gemTable.length).toBe(68)
|
|
expect(gemTable.all.length).toBe(68)
|
|
expect(gemTable.byCode.size).toBe(68)
|
|
})
|
|
|
|
it('verifies exactly 35 gems and 33 runes', () => {
|
|
if (!hasD2) return
|
|
|
|
expect(gemTable.gems.length).toBe(35)
|
|
expect(gemTable.runes.length).toBe(33)
|
|
|
|
expect(gemTable.gems.every(g => g.isGem && !g.isRune)).toBe(true)
|
|
expect(gemTable.runes.every(r => r.isRune && !r.isGem)).toBe(true)
|
|
|
|
// First gem is Chipped Amethyst (gcv), last gem is Perfect Skull (skz)
|
|
expect(gemTable.gems[0]?.code).toBe('gcv')
|
|
expect(gemTable.gems[0]?.name).toBe('Chipped Amethyst')
|
|
expect(gemTable.gems[34]?.code).toBe('skz')
|
|
expect(gemTable.gems[34]?.name).toBe('Perfect Skull')
|
|
|
|
// First rune is El Rune (r01), last rune is Zod Rune (r33)
|
|
expect(gemTable.runes[0]?.code).toBe('r01')
|
|
expect(gemTable.runes[0]?.name).toBe('El Rune')
|
|
expect(gemTable.runes[32]?.code).toBe('r33')
|
|
expect(gemTable.runes[32]?.name).toBe('Zod Rune')
|
|
})
|
|
|
|
it('verifies Chipped Amethyst (gcv) modifier values against MPQ data', () => {
|
|
if (!hasD2) return
|
|
|
|
const gcv = gemTable.get('gcv')!
|
|
expect(gcv).toBeDefined()
|
|
expect(gcv.name).toBe('Chipped Amethyst')
|
|
|
|
// Weapon: att 40
|
|
expect(gcv.weaponMods).toEqual([
|
|
expect.objectContaining({ code: 'att', min: 40, max: 40 }),
|
|
])
|
|
|
|
// Helm: str 3
|
|
expect(gcv.helmMods).toEqual([
|
|
expect.objectContaining({ code: 'str', min: 3, max: 3 }),
|
|
])
|
|
|
|
// Shield: ac 8
|
|
expect(gcv.shieldMods).toEqual([
|
|
expect.objectContaining({ code: 'ac', min: 8, max: 8 }),
|
|
])
|
|
})
|
|
|
|
it('verifies integration modifier dispatch using real ItemTypes isA', () => {
|
|
if (!hasD2) return
|
|
|
|
const isAPred = (type: string, target: string) => isA(type, target, itemTypes)
|
|
|
|
// Sword -> weaponMods
|
|
const swordMods = getGemModifiers('gcv', 'swor', isAPred, gemTable)
|
|
expect(swordMods).toEqual([
|
|
expect.objectContaining({ code: 'att', min: 40, max: 40 }),
|
|
])
|
|
|
|
// Helm -> helmMods
|
|
const helmMods = getGemModifiers('gcv', 'helm', isAPred, gemTable)
|
|
expect(helmMods).toEqual([
|
|
expect.objectContaining({ code: 'str', min: 3, max: 3 }),
|
|
])
|
|
|
|
// Torso -> helmMods (CRITICAL: body armor gets helm mods)
|
|
const torsoMods = getGemModifiers('gcv', 'tors', isAPred, gemTable)
|
|
expect(torsoMods).toEqual([
|
|
expect.objectContaining({ code: 'str', min: 3, max: 3 }),
|
|
])
|
|
|
|
// Shield -> shieldMods
|
|
const shieldMods = getGemModifiers('gcv', 'shie', isAPred, gemTable)
|
|
expect(shieldMods).toEqual([
|
|
expect.objectContaining({ code: 'ac', min: 8, max: 8 }),
|
|
])
|
|
})
|
|
|
|
it('verifies runes with duration params (Tal Rune r07, Thul Rune r10)', () => {
|
|
if (!hasD2) return
|
|
|
|
const isAPred = (type: string, target: string) => isA(type, target, itemTypes)
|
|
|
|
// Tal Rune: poison length 125 frames (5s) on weapon
|
|
const talWeapon = getGemModifiers('r07', 'swor', isAPred, gemTable)
|
|
expect(talWeapon).toEqual([
|
|
expect.objectContaining({ code: 'dmg-pois', param: 125, min: 154, max: 154 }),
|
|
])
|
|
|
|
// Thul Rune: cold length 75 frames (3s) on weapon
|
|
const thulWeapon = getGemModifiers('r10', 'swor', isAPred, gemTable)
|
|
expect(thulWeapon).toEqual([
|
|
expect.objectContaining({ code: 'dmg-cold', param: 75, min: 3, max: 14 }),
|
|
])
|
|
|
|
// Tal Rune on shield: res-pois 35
|
|
const talShield = getGemModifiers('r07', 'shie', isAPred, gemTable)
|
|
expect(talShield).toEqual([
|
|
expect.objectContaining({ code: 'res-pois', min: 35, max: 35 }),
|
|
])
|
|
})
|
|
|
|
it('verifies real ItemTypes socket tiers with getMaxSockets', () => {
|
|
if (!hasD2) return
|
|
|
|
const sworDef = itemTypes.byCode.get('swor')!
|
|
expect(sworDef).toBeDefined()
|
|
expect(sworDef.maxSock1).toBe(3)
|
|
expect(sworDef.maxSock25).toBe(4)
|
|
expect(sworDef.maxSock40).toBe(6)
|
|
|
|
// Crystal sword (base gemsockets = 6)
|
|
expect(getMaxSockets(6, sworDef, 24)).toBe(3)
|
|
expect(getMaxSockets(6, sworDef, 25)).toBe(4)
|
|
expect(getMaxSockets(6, sworDef, 39)).toBe(4)
|
|
expect(getMaxSockets(6, sworDef, 40)).toBe(6)
|
|
|
|
const helmDef = itemTypes.byCode.get('helm')!
|
|
expect(helmDef).toBeDefined()
|
|
expect(helmDef.maxSock1).toBe(2)
|
|
expect(helmDef.maxSock25).toBe(2)
|
|
expect(helmDef.maxSock40).toBe(3)
|
|
|
|
// Crown (base gemsockets = 3)
|
|
expect(getMaxSockets(3, helmDef, 24)).toBe(2)
|
|
expect(getMaxSockets(3, helmDef, 25)).toBe(2)
|
|
expect(getMaxSockets(3, helmDef, 39)).toBe(2)
|
|
expect(getMaxSockets(3, helmDef, 40)).toBe(3)
|
|
})
|
|
})
|
|
})
|