diablo2-web/src/game/embedded-drop-tables.ts

116 lines
4.0 KiB
TypeScript

/**
* Embedded Diablo II 1.13c Drop Tables Provider.
*
* Provides a self-contained, zero-network, zero-MPQ implementation of DropTables
* using the canonical embedded 1.13c Blizzard data tables.
* This guarantees instantaneous load times across any static HTTP server (e.g. port 8080,
* file protocol, or headless production environments) without partial Range request failures.
*/
import {
RAW_TC_EX,
RAW_WEAPONS,
RAW_ARMOR,
RAW_MISC,
RAW_ITEM_RATIO,
RAW_ITEM_TYPES,
RAW_UNIQUE_ITEMS,
RAW_SET_ITEMS,
RAW_SETS,
RAW_MAGIC_PREFIX,
RAW_MAGIC_SUFFIX,
RAW_RARE_PREFIX,
RAW_RARE_SUFFIX,
} from '../data/canonical-drop-data.ts'
import { parseTable } from './acts.ts'
import { parseTreasureClassTable, type TreasureClassNode } from './treasure-class.ts'
import { parseWeaponsTable } from './weapons.ts'
import { parseArmorTable } from './armor.ts'
import { parseMiscTable } from './misc-items.ts'
import { parseItemRatioTable } from './item-ratio.ts'
import { parseUniqueItemsTable } from './unique-items.ts'
import { parseSetItemsTable, parseSetsTable } from './set-items.ts'
import { parseMagicAffixesTable } from './affixes.ts'
import { parseRareNamesTable } from './rare-names.ts'
import { parseItemTypesTable, isA } from './item-types.ts'
import { generateAutoTreasureClasses, autoTcToTreasureClassNode } from './auto-tc.ts'
import {
DifficultyLevelsTable,
DEFAULT_DIFFICULTY_LEVEL_ODDS,
type DifficultyLevelOdds,
CaseInsensitiveMap,
} from './item-upgrade.ts'
import type { DropTables } from './drop-pipeline.ts'
import type { ItemBase } from './items.ts'
import { CANONICAL_AUTOMAGIC_TABLE } from './automagic.ts'
let _cachedDropTables: DropTables | null = null
/**
* Returns the fully-initialized, canonical 1.13c DropTables.
* Initialized synchronously on first call (< 120ms) and memoized for all subsequent calls.
*/
export function getEmbeddedDropTables(): DropTables {
if (_cachedDropTables) {
return _cachedDropTables
}
const enc = new TextEncoder()
const tcTable = parseTreasureClassTable(parseTable(enc.encode(RAW_TC_EX)))
const weapons = parseWeaponsTable(parseTable(enc.encode(RAW_WEAPONS)))
const armor = parseArmorTable(parseTable(enc.encode(RAW_ARMOR)))
const misc = parseMiscTable(parseTable(enc.encode(RAW_MISC)))
const itemRatio = parseItemRatioTable(parseTable(enc.encode(RAW_ITEM_RATIO)))
const itemTypes = parseItemTypesTable(parseTable(enc.encode(RAW_ITEM_TYPES)))
const uniques = parseUniqueItemsTable(parseTable(enc.encode(RAW_UNIQUE_ITEMS)))
const sets = {
items: parseSetItemsTable(parseTable(enc.encode(RAW_SET_ITEMS))),
sets: parseSetsTable(parseTable(enc.encode(RAW_SETS))),
}
const magicAffixes = {
prefixes: parseMagicAffixesTable(parseTable(enc.encode(RAW_MAGIC_PREFIX)), true),
suffixes: parseMagicAffixesTable(parseTable(enc.encode(RAW_MAGIC_SUFFIX)), false),
}
const rareNames = {
prefixes: parseRareNamesTable(parseTable(enc.encode(RAW_RARE_PREFIX))),
suffixes: parseRareNamesTable(parseTable(enc.encode(RAW_RARE_SUFFIX))),
}
const rawAuto = generateAutoTreasureClasses(itemTypes, armor, weapons)
const autoTcTable = new Map<string, TreasureClassNode>()
for (const [k, v] of rawAuto) {
autoTcTable.set(k, autoTcToTreasureClassNode(v))
}
const isAPredicate = (type: string, target: string): boolean => isA(type, target, itemTypes)
const getBase = (code: string): ItemBase | undefined => weapons.get(code) ?? armor.get(code) ?? misc.get(code)
const oddsMap = new CaseInsensitiveMap<DifficultyLevelOdds>()
for (const [k, v] of Object.entries(DEFAULT_DIFFICULTY_LEVEL_ODDS)) {
oddsMap.set(k, v)
}
const difficultyLevels = new DifficultyLevelsTable(Object.values(DEFAULT_DIFFICULTY_LEVEL_ODDS), oddsMap)
_cachedDropTables = {
tcTable,
autoTcTable,
weapons,
armor,
misc,
itemRatio,
difficultyLevels,
uniques,
sets,
magicAffixes,
rareNames,
autoMagic: CANONICAL_AUTOMAGIC_TABLE,
itemTypes,
monsterKinds: new Map(),
superUniques: new Map(),
isA: isAPredicate,
getBase,
}
return _cachedDropTables!
}