102 lines
3.7 KiB
TypeScript
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);
|
|
});
|
|
|
|
});
|