diablo2-web/tests/resolve-type1.test.ts

102 lines
3.7 KiB
TypeScript

import { describe, expect, test } from 'vitest';
import { resolveDs1Object, OBJECT_ROOT } from '../src/game/objects';
import type { ObjectsTable, MonstersTable } from '../src/game/objects';
import { OBJECT_TYPE_MONSTER, OBJECT_TYPE_OBJECT } from '../src/game/object-lookup';
describe('resolveDs1Object (type 1 rules)', () => {
const emptyObjectsTable: ObjectsTable = {
table: { header: [], rows: [] },
rows: [],
byId: new Map()
};
const mockMonsters = (): MonstersTable => {
// Let's create a preset map and stats map.
// We need Act 1, Id 0 -> 'dummyplace'
// Act 1, Id 1 -> 'warriv1' -> has code WA
// Act 2, Id 0 -> 'warriv1' -> has code NO_WA
// Act 1, Id 2 -> 'place_nothing' -> has no row
// We only need to populate `presetPlaceByAct` for Act 1 and 2.
const presetPlaceByAct = new Map<string, string[]>();
presetPlaceByAct.set('1', ['dummyplace', 'warriv1', 'place_nothing']);
presetPlaceByAct.set('2', ['warriv1']); // same id but diff act
const statsById = new Map<string, string[]>();
// Assume columns are 0: Id, 8: Code, 5: NameStr? (Whatever cell reading does, wait, cell reads by column header offset!)
// So we need proper D2Table for `monsters.stats`.
const statsHeader = ['Id', 'NameStr', 'Code'];
const statsRows = [
['warriv1', 'Warriv', 'WA'],
['dummyplace', '', 'DM'], // has place and art
// place_nothing is absent
];
statsById.set('warriv1', statsRows[0]);
statsById.set('dummyplace', statsRows[1]);
const statsTable = { header: statsHeader, rows: statsRows };
const presetTable = { header: ['Act', 'Place'], rows: [] }; // The array wasn't used in code if presetPlaceByAct is there, but cell needs headers!
return {
stats: statsTable,
preset: presetTable,
presetPlaceByAct,
statsById
};
};
test('resolves named NPC with code and name', () => {
const monsters = mockMonsters();
const res = resolveDs1Object(emptyObjectsTable, 1, OBJECT_TYPE_MONSTER, 1, monsters);
expect(res.kind).toBe('npc');
expect(res.token).toBe('WA');
expect(res.name).toBe('Warriv');
expect(res.artless).toBe(false);
});
test('resolves place with no stats row as artless monster/npc', () => {
const monsters = mockMonsters();
const res = resolveDs1Object(emptyObjectsTable, 1, OBJECT_TYPE_MONSTER, 2, monsters);
// place_nothing is not in statsById -> code is '' -> artless is true
expect(res.kind).toBe('npc');
expect(res.token).toBe('');
expect(res.name).toBe('place_nothing');
expect(res.artless).toBe(true);
});
test('returns default monster if objectId out of range', () => {
const monsters = mockMonsters();
const res = resolveDs1Object(emptyObjectsTable, 1, OBJECT_TYPE_MONSTER, 999, monsters);
expect(res.kind).toBe('monster');
expect(res.token).toBe('');
expect(res.artless).toBe(true);
});
test('act filtering produces different results for same id', () => {
const monsters = mockMonsters();
// In Act 1, Id 0 -> dummyplace (token DM)
const res1 = resolveDs1Object(emptyObjectsTable, 1, OBJECT_TYPE_MONSTER, 0, monsters);
expect(res1.token).toBe('DM');
// In Act 2, Id 0 -> warriv1 (token WA)
const res2 = resolveDs1Object(emptyObjectsTable, 2, OBJECT_TYPE_MONSTER, 0, monsters);
expect(res2.token).toBe('WA');
});
test('omitted monsters parameter restores legacy behavior (dropping type 1)', () => {
// If monsters is undefined, type 1 falls back to artless monster
const res = resolveDs1Object(emptyObjectsTable, 1, OBJECT_TYPE_MONSTER, 1, undefined);
expect(res.kind).toBe('monster');
expect(res.token).toBe('');
expect(res.artless).toBe(true);
});
});