/** * Unit tests for the 11 Diablo II DRLG Maze Special Passes. * * Covers: * 1. DRLGMAZE_PlaceAct2TombStuff (Tal Rasha True/False Tombs, Kaa, Cube, Leatherarm, Waypoint) * 2. DRLGMAZE_PlaceAct2LairStuff (Maggot Lair Coldworm Tight Spot & Staff of Kings Treasure) * 3. DRLGMAZE_PlaceAct3DungeonStuff (Flayer Dungeon Brain chest, Kurast Temples Lam Esen's Tome) * 4. DRLGMAZE_PlaceAct3SewerStuff (Kurast Sewers Drain lever descent & Chest) * 5. DRLGMAZE_PlaceAct3MephistoStuff (Durance of Hate 3 Mephisto Complex, Durance 2 Waypoint) * 6. DRLGMAZE_PlaceAct5TempleStuff (Halls of Vaught Nihlathak Final Room, Halls of Pain Waypoint) * 7. DRLGMAZE_PlaceAct5BaalStuff (Throne of Destruction Baal Entrance/ThroneRoom, Worldstone Waypoint) * 8. DRLGMAZE_PlaceAct5IceStuff (Frozen River Anya platform, Drifter Cavern / Icy Cellar Pools, Waypoints) * 9. DRLGMAZE_PlaceAct1Barracks (Monastery Barracks Smith Forge & Court Connect) * 10. DRLGMAZE_PlaceAct4Lava (River of Flame Hellforge, Bridges 1 & 2, Warp, Lava X fillers) * 11. DRLGMAZE_ScanReplaceSpecialAct2SewersPresets (Lut Gholein Sewers Radament's Lair, Dual Entrances, Waypoint) * 12. Full generateMaze integration tests verifying specialPassesApplied and the * per-level-type `unimplementedPasses` gap list. */ import { describe, expect, test } from 'vitest' import type { Ds1, Ds1Cell, Ds1Object } from '../src/formats/ds1.ts' import { generateMaze, classifyMazePieceName, MAZE_LEVEL_TYPE_PROFILES, UNIMPLEMENTED_PASSES, unimplementedPassesFor, type MazePiece, type MazePieceKind, type MazeRequest, } from '../src/game/maze.ts' import { runMazeSpecialPasses, type SpecialPassName, } from '../src/game/maze-special-passes.ts' /** * Construct a synthetic minimal Ds1 map. */ function mockDs1(width = 10, height = 10, objects: Ds1Object[] = []): Ds1 { const cells: Ds1Cell[][] = [] for (let y = 0; y < height; y++) { const row: Ds1Cell[] = [] for (let x = 0; x < width; x++) { row.push({ walls: [{ prop1: 1, sequence: 0, style: 0, type: 0, unknown1: 0, unknown2: 0, hidden: false }], floors: [{ prop1: 1, sequence: 0, style: 0, unknown1: 0, unknown2: 0, hidden: false }], shadows: [{ prop1: 0, sequence: 0, style: 0, unknown1: 0, unknown2: 0, hidden: false }], substitutions: [], }) } cells.push(row) } return { version: 18, width, height, act: 1, substitutionType: 0, wallLayers: 1, floorLayers: 1, cells, objects, npcPathOffset: null, } } /** * Construct a synthetic MazePiece. */ function mockPiece( name: string, kind: MazePieceKind, sides = '', objects: Ds1Object[] = [], width = 10, height = 10, ): MazePiece { return { name, kind, sides, levels: [mockDs1(width, height, objects)], } } /** * Create a full standard piece set for a level type so maze generation has * all 15 basic room shapes plus entrance and common directions. */ function createStandardPieceSet(levelTypeName: string, extraPieces: MazePiece[] = []): MazePiece[] { const SIDES = [ 'N', 'S', 'E', 'W', 'NE', 'NW', 'SE', 'SW', 'NS', 'EW', 'NSE', 'NSW', 'NEW', 'SEW', 'NSEW', ] const pieces: MazePiece[] = [ mockPiece(`${levelTypeName} Entrance`, 'entrance', ''), ...SIDES.map(s => mockPiece(`${levelTypeName} ${s}`, 'room', s)), ...['N', 'S', 'E', 'W'].map(s => mockPiece(`${levelTypeName} Prev ${s}`, 'prev', s, [{ type: 2, id: 1, x: 25, y: 25, flags: 0 }])), ...['N', 'S', 'E', 'W'].map(s => mockPiece(`${levelTypeName} Next ${s}`, 'next', s, [{ type: 2, id: 2, x: 25, y: 25, flags: 0 }])), ...['N', 'S', 'E', 'W'].map(s => mockPiece(`${levelTypeName} Down ${s}`, 'down', s, [{ type: 2, id: 3, x: 25, y: 25, flags: 0 }])), ...['N', 'S', 'E', 'W'].map(s => mockPiece(`${levelTypeName} Theme ${s}`, 'theme', s)), ...extraPieces, ] return pieces } describe('Diablo II DRLG Maze Special Passes', () => { test('UNIMPLEMENTED_PASSES is empty when all maze profiles define specials', () => { const emptySpecials = MAZE_LEVEL_TYPE_PROFILES .filter(profile => profile.specials.length === 0 && profile.singleRoom === undefined) .map(profile => profile.levelTypeName) expect(emptySpecials).toEqual([]) expect(UNIMPLEMENTED_PASSES).toEqual([]) }) test('unimplementedPassesFor is empty for a fully transcribed level type', () => { expect(unimplementedPassesFor('Act 1 - Cave')).toEqual([]) expect(unimplementedPassesFor('Act 2 - Tomb')).toEqual([]) expect(unimplementedPassesFor('Act 2 - Harem')).toEqual([]) expect(unimplementedPassesFor('Act 2 - Basement')).toEqual([]) expect(unimplementedPassesFor('Act 3 - Spider')).toEqual([]) expect(unimplementedPassesFor('Act 5 - Lava')).toEqual([]) }) test('unimplementedPassesFor reports a level type that has no profile at all', () => { expect(unimplementedPassesFor('Act 9 - Nowhere')) .toEqual(['DRLGMAZE_GenerateLevel profile (Act 9 - Nowhere)']) }) test('generateMaze reports empty unimplementedPasses for transcribed levels', () => { const spider = generateMaze({ levelId: 84, levelName: 'Spider Cavern', sectionSize: 10, minRooms: 5, merge: 500, seed: 8484, pieces: createStandardPieceSet('Act 3 - Spider'), }) expect(spider.stats.unimplementedPasses).toEqual([]) // The other two diagnostic channels must travel with it. expect(Array.isArray(spider.stats.unresolvedRoles)).toBe(true) expect(Array.isArray(spider.stats.notes)).toBe(true) const cave = generateMaze({ levelId: 8, levelName: 'Den of Evil', sectionSize: 10, minRooms: 5, merge: 500, seed: 8484, pieces: createStandardPieceSet('Act 1 - Cave'), }) expect(cave.stats.unimplementedPasses).toEqual([]) }) /* ----------------------------------------------------------------------- * * 1. Act 2 - Tomb * ----------------------------------------------------------------------- */ describe('Pass 1: DRLGMAZE_PlaceAct2TombStuff', () => { const tombExtraPieces = [ mockPiece('Act 2 - Tomb Talrasha N', 'quest', 'N', [{ type: 2, id: 152, x: 25, y: 25, flags: 0 }]), // Horadric Staff Orifice mockPiece('Act 2 - Tomb Chest N', 'treasure', 'N', [{ type: 2, id: 397, x: 25, y: 25, flags: 0 }]), // Golden chest mockPiece('Act 2 - Tomb Kaa N', 'quest', 'N', [{ type: 1, id: 250, x: 25, y: 25, flags: 0 }]), // Ancient Kaa mockPiece('Act 2 - Tomb Cube N', 'treasure', 'N', [{ type: 2, id: 354, x: 25, y: 25, flags: 0 }]), // Horadric Cube mockPiece('Act 2 - Tomb Leatherarm N', 'quest', 'N'), mockPiece('Act 2 - Tomb Treasure N', 'treasure', 'N'), mockPiece('Act 2 - Tomb Waypoint N', 'waypoint', 'N', [{ type: 2, id: 156, x: 25, y: 25, flags: 0 }]), mockPiece('Act 2 - Tomb Prev NSW', 'prev', 'NSW'), mockPiece('Act 2 - Tomb Prev NEW', 'prev', 'NEW'), mockPiece('Act 2 - Tomb Prev NSE', 'prev', 'NSE'), mockPiece('Act 2 - Tomb Prev SEW', 'prev', 'SEW'), ] test('True Tomb (staffTombLevelId): places Talrasha Horadric Staff orifice', () => { const pieces = createStandardPieceSet('Act 2 - Tomb', tombExtraPieces) const res = generateMaze({ levelId: 66, levelName: 'Tal Rashas Tomb 1', sectionSize: 10, minRooms: 5, merge: 500, seed: 12345, pieces, staffTombLevelId: 66, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct2TombStuff') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] const hasTalrasha = specials.some(s => s.kind === 'quest') expect(hasTalrasha).toBe(true) // Level size is tripled for staff tomb expect(res.stats.targetRooms).toBe(15) }) test('False Tombs (67..72): places golden chest room', () => { const pieces = createStandardPieceSet('Act 2 - Tomb', tombExtraPieces) const res = generateMaze({ levelId: 67, levelName: 'Tal Rashas Tomb 2', sectionSize: 10, minRooms: 5, merge: 500, seed: 54321, pieces, staffTombLevelId: 66, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct2TombStuff') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'treasure')).toBe(true) }) test('Halls of the Dead 2 (level 59): places Waypoint', () => { const pieces = createStandardPieceSet('Act 2 - Tomb', tombExtraPieces) const res = generateMaze({ levelId: 59, levelName: 'Halls of the Dead 2', sectionSize: 10, minRooms: 5, merge: 500, seed: 99999, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct2TombStuff') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'waypoint')).toBe(true) }) test('Halls of the Dead 3 (level 60): places Horadric Cube chest', () => { const pieces = createStandardPieceSet('Act 2 - Tomb', tombExtraPieces) const res = generateMaze({ levelId: 60, levelName: 'Halls of the Dead 3', sectionSize: 10, minRooms: 5, merge: 500, seed: 88888, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct2TombStuff') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'quest' || s.kind === 'treasure')).toBe(true) }) test('Boss Tomb: places Ancient Kaa the Soulless', () => { const pieces = createStandardPieceSet('Act 2 - Tomb', tombExtraPieces) const res = generateMaze({ levelId: 68, levelName: 'Tal Rashas Tomb 3', sectionSize: 10, minRooms: 5, merge: 500, seed: 77777, pieces, staffTombLevelId: 66, bossTombLevelId: 68, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct2TombStuff') expect(res.stats.targetRooms).toBe(10) // Doubled for boss tomb }) }) /* ----------------------------------------------------------------------- * * 2. Act 2 - Lair * ----------------------------------------------------------------------- */ describe('Pass 2: DRLGMAZE_PlaceAct2LairStuff', () => { const lairExtraPieces = [ mockPiece('Act 2 - Lair Tight Spot S', 'quest', 'S', [{ type: 1, id: 284, x: 25, y: 25, flags: 0 }]), // Coldworm mockPiece('Act 2 - Lair Treasure W', 'treasure', 'W', [{ type: 2, id: 356, x: 25, y: 25, flags: 0 }]), // Staff of Kings chest ] test('Maggot Lair 3 (level 64): places Coldworm Tight Spot and Staff of Kings chest', () => { const pieces = createStandardPieceSet('Act 2 - Lair', lairExtraPieces) const res = generateMaze({ levelId: 64, levelName: 'Maggot Lair 3', sectionSize: 10, minRooms: 6, merge: 500, seed: 42, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct2LairStuff') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'quest')).toBe(true) expect(specials.some(s => s.kind === 'treasure')).toBe(true) expect(res.level.objects.some(o => o.type === 1 && o.id === 284)).toBe(true) expect(res.level.objects.some(o => o.type === 2 && o.id === 356)).toBe(true) }) test('Maggot Lair 1 (level 62): places regular next and prev', () => { const pieces = createStandardPieceSet('Act 2 - Lair', lairExtraPieces) const res = generateMaze({ levelId: 62, levelName: 'Maggot Lair 1', sectionSize: 10, minRooms: 5, merge: 500, seed: 101, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct2LairStuff') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'next')).toBe(true) }) }) /* ----------------------------------------------------------------------- * * 3. Act 3 - Dungeon * ----------------------------------------------------------------------- */ describe('Pass 3: DRLGMAZE_PlaceAct3DungeonStuff', () => { const dungeonExtraPieces = [ mockPiece('Act 3 - Dungeon Treasure 2', 'treasure', '', [{ type: 2, id: 406, x: 25, y: 25, flags: 0 }]), // Khalim's Brain chest mockPiece('Act 3 - Dungeon Treasure 1', 'treasure', ''), mockPiece('Act 3 - Temple 1', 'quest', '', [{ type: 2, id: 193, x: 25, y: 25, flags: 0 }]), // Lam Esen's Tome altar ] test("Flayer Dungeon 3 (level 91): places Khalim's Brain chest (Treasure 2)", () => { const pieces = createStandardPieceSet('Act 3 - Dungeon', dungeonExtraPieces) const res = generateMaze({ levelId: 91, levelName: 'Flayer Dungeon 3', levelTypeName: 'Act 3 - Dungeon', sectionSize: 10, minRooms: 6, merge: 500, seed: 31415, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct3DungeonStuff') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'treasure')).toBe(true) expect(res.level.objects.some(o => o.type === 2 && o.id === 406)).toBe(true) }) test("Ruined Temple (level 94): places Lam Esen's Tome altar (Temple 1)", () => { const pieces = createStandardPieceSet('Act 3 - Dungeon', dungeonExtraPieces) const res = generateMaze({ levelId: 94, levelName: 'Ruined Temple', levelTypeName: 'Act 3 - Dungeon', sectionSize: 10, minRooms: 5, merge: 500, seed: 27182, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct3DungeonStuff') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'quest')).toBe(true) expect(res.level.objects.some(o => o.type === 2 && o.id === 193)).toBe(true) }) }) /* ----------------------------------------------------------------------- * * 4. Act 3 - Sewer * ----------------------------------------------------------------------- */ describe('Pass 4: DRLGMAZE_PlaceAct3SewerStuff', () => { const sewerExtraPieces = [ mockPiece('Act 3 - Sewer Drain N', 'next', 'N', [{ type: 2, id: 366, x: 25, y: 25, flags: 0 }]), // Drain lever to Level 2 mockPiece('Act 3 - Sewer Chest S', 'treasure', 'S'), mockPiece('Act 3 - Sewer Treasure X', 'treasure', ''), mockPiece('Act 3 - Sewer NW', 'room', 'NW'), mockPiece('Act 3 - Sewer NE', 'room', 'NE'), mockPiece('Act 3 - Sewer SW', 'room', 'SW'), mockPiece('Act 3 - Sewer SE', 'room', 'SE'), mockPiece('Act 3 - Sewer Prev NW', 'prev', 'NW'), mockPiece('Act 3 - Sewer Prev NE', 'prev', 'NE'), mockPiece('Act 3 - Sewer Prev SW', 'prev', 'SW'), mockPiece('Act 3 - Sewer Prev SE', 'prev', 'SE'), ] test('Kurast Sewers 1 (level 92): places Drain lever and Chest', () => { const pieces = createStandardPieceSet('Act 3 - Sewer', sewerExtraPieces) const res = generateMaze({ levelId: 92, levelName: 'Kurast Sewers 1', sectionSize: 10, minRooms: 18, merge: 500, seed: 777, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct3SewerStuff') expect(res.level.objects.some(o => o.type === 2 && o.id === 366)).toBe(true) }) }) /* ----------------------------------------------------------------------- * * 5. Act 3 - Mephisto * ----------------------------------------------------------------------- */ describe('Pass 5: DRLGMAZE_PlaceAct3MephistoStuff', () => { const mephistoExtraPieces = [ mockPiece('Act 3 - Mephisto Complex', 'quest', '', [{ type: 2, id: 251, x: 50, y: 50, flags: 0 }]), // Red portal to Act 4 mockPiece('Act 3 - Mephisto Waypoint N', 'waypoint', 'N', [{ type: 2, id: 237, x: 25, y: 25, flags: 0 }]), mockPiece('Act 3 - Mephisto Prev N', 'prev', 'N'), mockPiece('Act 3 - Mephisto Next N', 'next', 'N'), ] test('Durance of Hate 3 (level 102): places Mephisto Complex', () => { const pieces = createStandardPieceSet('Act 3 - Kurast', mephistoExtraPieces) const res = generateMaze({ levelId: 102, levelName: 'Durance of Hate 3', levelTypeName: 'Act 3 - Kurast', sectionSize: 10, minRooms: 5, merge: 500, seed: 666, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct3MephistoStuff') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'quest')).toBe(true) expect(res.level.objects.some(o => o.type === 2 && o.id === 251)).toBe(true) }) test('Durance of Hate 2 (level 101): places Waypoint', () => { const pieces = createStandardPieceSet('Act 3 - Kurast', mephistoExtraPieces) const res = generateMaze({ levelId: 101, levelName: 'Durance of Hate 2', levelTypeName: 'Act 3 - Kurast', sectionSize: 10, minRooms: 6, merge: 500, seed: 10101, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct3MephistoStuff') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'waypoint')).toBe(true) }) }) /* ----------------------------------------------------------------------- * * 6. Act 5 - Temple * ----------------------------------------------------------------------- */ describe('Pass 6: DRLGMAZE_PlaceAct5TempleStuff', () => { const templeExtraPieces = [ mockPiece('Act 5 - Temple Final Room', 'quest', '', [{ type: 1, id: 526, x: 25, y: 25, flags: 0 }]), // Nihlathak mockPiece('Act 5 - Temple NE Waypoint', 'waypoint', 'NE'), mockPiece('Act 5 - Temple NW Waypoint', 'waypoint', 'NW'), mockPiece('Act 5 - Temple SW Waypoint', 'waypoint', 'SW'), mockPiece('Act 5 - Temple SE Waypoint', 'waypoint', 'SE'), mockPiece('Act 5 - Temple NE Down', 'down', 'NE'), mockPiece('Act 5 - Temple NW Down', 'down', 'NW'), mockPiece('Act 5 - Temple SW Down', 'down', 'SW'), mockPiece('Act 5 - Temple SE up', 'prev', 'SE'), mockPiece('Act 5 - Temple SE', 'room', 'SE'), mockPiece('Act 5 - Temple NE', 'room', 'NE'), mockPiece('Act 5 - Temple NW', 'room', 'NW'), mockPiece('Act 5 - Temple SW', 'room', 'SW'), ] test('Halls of Vaught (level 124): places Nihlathak Final Room altar', () => { const pieces = createStandardPieceSet('Act 5 - Temple', templeExtraPieces) const res = generateMaze({ levelId: 124, levelName: 'Halls of Vaught', sectionSize: 10, minRooms: 5, merge: 500, seed: 124124, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct5TempleStuff') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'quest')).toBe(true) expect(res.level.objects.some(o => o.type === 1 && o.id === 526)).toBe(true) }) test('Halls of Pain (level 123): places Waypoint and stairs', () => { const pieces = createStandardPieceSet('Act 5 - Temple', templeExtraPieces) const res = generateMaze({ levelId: 123, levelName: 'Halls of Pain', sectionSize: 10, minRooms: 5, merge: 500, seed: 123123, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct5TempleStuff') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'waypoint')).toBe(true) }) test('Halls of Pain (level 123): generates all 4 waypoint directions including NE across seeds', () => { const pieces = createStandardPieceSet('Act 5 - Temple', templeExtraPieces) const observedDirs = new Set() for (let seed = 1; seed <= 50; seed++) { const res = generateMaze({ levelId: 123, levelName: 'Halls of Pain', sectionSize: 10, minRooms: 5, merge: 500, seed, pieces, }) const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] const wp = specials.find(s => s.kind === 'waypoint') if (wp !== undefined) { observedDirs.add(wp.sides) } } expect(observedDirs).toContain('NE') expect(observedDirs).toContain('NW') expect(observedDirs).toContain('SW') expect(observedDirs).toContain('SE') }) }) /* ----------------------------------------------------------------------- * * 7. Act 5 - Baal * ----------------------------------------------------------------------- */ describe('Pass 7: DRLGMAZE_PlaceAct5BaalStuff', () => { const baalExtraPieces = [ mockPiece('Act 5 - ThroneRoom', 'quest', '', [{ type: 2, id: 563, x: 25, y: 25, flags: 0 }]), // Baal Throne mockPiece('Act 5 - Baal Waypoint N', 'waypoint', 'N'), mockPiece('Act 5 - Baal Prev NSW', 'prev', 'NSW'), mockPiece('Act 5 - Baal Prev NEW', 'prev', 'NEW'), mockPiece('Act 5 - Baal Prev NSE', 'prev', 'NSE'), mockPiece('Act 5 - Baal Prev SEW', 'prev', 'SEW'), ] test('Throne of Destruction (level 131): places ThroneRoom', () => { const pieces = createStandardPieceSet('Act 5 - Baal', baalExtraPieces) const res = generateMaze({ levelId: 131, levelName: 'Throne of Destruction', levelTypeName: 'Act 5 - Baal', sectionSize: 10, minRooms: 5, merge: 500, seed: 131131, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct5BaalStuff') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'quest')).toBe(true) expect(res.level.objects.some(o => o.type === 2 && o.id === 563)).toBe(true) }) test('Worldstone Keep 2 (level 129): places Waypoint', () => { const pieces = createStandardPieceSet('Act 5 - Baal', baalExtraPieces) const res = generateMaze({ levelId: 129, levelName: 'Worldstone Keep 2', levelTypeName: 'Act 5 - Baal', sectionSize: 10, minRooms: 5, merge: 500, seed: 129129, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct5BaalStuff') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'waypoint')).toBe(true) }) }) /* ----------------------------------------------------------------------- * * 8. Act 5 - Ice * ----------------------------------------------------------------------- */ describe('Pass 8: DRLGMAZE_PlaceAct5IceStuff', () => { const iceExtraPieces = [ mockPiece('Act 5 - Ice River A', 'quest', '', [{ type: 2, id: 460, x: 25, y: 25, flags: 0 }]), // Frozen Anya mockPiece('Act 5 - Ice River B', 'quest', '', [{ type: 2, id: 460, x: 25, y: 25, flags: 0 }]), mockPiece('Act 5 - Ice Pool A', 'quest', ''), mockPiece('Act 5 - Ice Pool B', 'quest', ''), mockPiece('Act 5 - Ice waypoint N', 'waypoint', 'N'), ] test('Frozen River (level 114): places Anya platform', () => { const pieces = createStandardPieceSet('Act 5 - Ice Caves', iceExtraPieces) const res = generateMaze({ levelId: 114, levelName: 'Frozen River', levelTypeName: 'Act 5 - Ice Caves', sectionSize: 10, minRooms: 1, // Single-room preset in profile merge: 500, seed: 114114, pieces, }) // Level 114 resolves single room preset Act 5 - Ice River A expect(res.level.objects.some(o => o.type === 2 && o.id === 460)).toBe(true) }) test('Glacial Trail (level 115): places Waypoint', () => { const pieces = createStandardPieceSet('Act 5 - Ice Caves', iceExtraPieces) const res = generateMaze({ levelId: 115, levelName: 'Glacial Trail', levelTypeName: 'Act 5 - Ice Caves', sectionSize: 10, minRooms: 5, merge: 500, seed: 115115, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct5IceStuff') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'waypoint')).toBe(true) }) }) /* ----------------------------------------------------------------------- * * 9. Act 1 - Barracks * ----------------------------------------------------------------------- */ describe('Pass 9: DRLGMAZE_PlaceAct1Barracks', () => { const barracksExtraPieces = [ mockPiece('Act 1 - Barracks Forge N', 'quest', 'N', [{ type: 2, id: 108, x: 25, y: 25, flags: 0 }]), // Charsi's Malus forge mockPiece('Act 1 - Barracks Forge S', 'quest', 'S', [{ type: 2, id: 108, x: 25, y: 25, flags: 0 }]), mockPiece('Act 1 - Barracks Forge E', 'quest', 'E', [{ type: 2, id: 108, x: 25, y: 25, flags: 0 }]), mockPiece('Act 1 - Barracks Forge W', 'quest', 'W', [{ type: 2, id: 108, x: 25, y: 25, flags: 0 }]), mockPiece('Act 1 - Barracks Court Connect', 'prev', '', [{ type: 2, id: 1, x: 10, y: 10, flags: 0 }]), // Entrance from Cloister ] test('Monastery Barracks (level 28): places Court Connect and Smith Forge', () => { const pieces = createStandardPieceSet('Act 1 - Barracks', barracksExtraPieces) const res = generateMaze({ levelId: 28, levelName: 'Barracks', sectionSize: 10, sectionHeight: 14, minRooms: 6, merge: 500, seed: 2828, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct1Barracks') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'quest')).toBe(true) expect(specials.some(s => s.kind === 'prev')).toBe(true) expect(res.level.objects.some(o => o.type === 2 && o.id === 108)).toBe(true) }) }) /* ----------------------------------------------------------------------- * * 10. Act 4 - Lava * ----------------------------------------------------------------------- */ describe('Pass 10: DRLGMAZE_PlaceAct4Lava', () => { const lavaExtraPieces = [ mockPiece('Act 4 - Lava Warp N', 'prev', 'N', [{ type: 2, id: 255, x: 25, y: 25, flags: 0 }]), mockPiece('Act 4 - Bridge 1', 'next', '', [{ type: 2, id: 10, x: 25, y: 25, flags: 0 }]), mockPiece('Act 4 - Bridge 2', 'next', '', [{ type: 2, id: 11, x: 25, y: 25, flags: 0 }]), mockPiece('Act 4 - Lava Forge W', 'quest', 'W', [{ type: 2, id: 376, x: 25, y: 25, flags: 0 }]), // Hellforge mockPiece('Act 4 - Lava Forge E', 'quest', 'E', [{ type: 2, id: 376, x: 25, y: 25, flags: 0 }]), mockPiece('Act 4 - Lava X', 'room', ''), // Filler ] test('River of Flame (level 107): places Hellforge, Bridges, Warp and Lava X filler', () => { const pieces = createStandardPieceSet('Act 4 - Lava', lavaExtraPieces) const res = generateMaze({ levelId: 107, levelName: 'River of Flame', levelTypeName: 'Act 4 - Lava', sectionSize: 10, minRooms: 6, merge: 500, seed: 107107, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_PlaceAct4Lava') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'quest')).toBe(true) expect(res.level.objects.some(o => o.type === 2 && o.id === 376)).toBe(true) // Confirm Lava X filler spaces were placed expect(specials.some(s => s.sides === 'filler')).toBe(true) }) }) /* ----------------------------------------------------------------------- * * 11. Act 2 - Sewers * ----------------------------------------------------------------------- */ describe('Pass 11: DRLGMAZE_ScanReplaceSpecialAct2SewersPresets', () => { const sewer2ExtraPieces = [ mockPiece("Act 2 - Sewer Radament's Lair N", 'quest', 'N', [{ type: 1, id: 229, x: 25, y: 25, flags: 0 }]), // Radament mockPiece("Act 2 - Sewer Radament's Lair S", 'quest', 'S', [{ type: 1, id: 229, x: 25, y: 25, flags: 0 }]), mockPiece("Act 2 - Sewer Radament's Lair E", 'quest', 'E', [{ type: 1, id: 229, x: 25, y: 25, flags: 0 }]), mockPiece("Act 2 - Sewer Radament's Lair W", 'quest', 'W', [{ type: 1, id: 229, x: 25, y: 25, flags: 0 }]), mockPiece('Act 2 - Sewer Prev E', 'prev', 'E'), // City entrance mockPiece('Act 2 - Sewer Prev NS', 'prev', 'NS'), // Dock entrance mockPiece('Act 2 - Sewer Waypoint N', 'waypoint', 'N', [{ type: 2, id: 156, x: 25, y: 25, flags: 0 }]), mockPiece('Act 2 - Sewer Chest N', 'treasure', 'N', [{ type: 2, id: 355, x: 25, y: 25, flags: 0 }]), ] test("Sewers Level 3 (level 49): places Radament's Lair", () => { const pieces = createStandardPieceSet('Act 2 - Sewer', sewer2ExtraPieces) const res = generateMaze({ levelId: 49, levelName: 'Sewers Level 3', sectionSize: 10, minRooms: 6, merge: 500, seed: 4949, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_ScanReplaceSpecialAct2SewersPresets') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'quest')).toBe(true) expect(res.level.objects.some(o => o.type === 1 && o.id === 229)).toBe(true) }) test('Sewers Level 1 (level 47): places dock and city entrances', () => { const pieces = createStandardPieceSet('Act 2 - Sewer', sewer2ExtraPieces) const res = generateMaze({ levelId: 47, levelName: 'Sewers Level 1', sectionSize: 10, minRooms: 6, merge: 500, seed: 4747, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_ScanReplaceSpecialAct2SewersPresets') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] const prevs = specials.filter(s => s.kind === 'prev') expect(prevs.length).toBeGreaterThanOrEqual(1) }) test('Sewers Level 2 (level 48): places Waypoint', () => { const pieces = createStandardPieceSet('Act 2 - Sewer', sewer2ExtraPieces) const res = generateMaze({ levelId: 48, levelName: 'Sewers Level 2', sectionSize: 10, minRooms: 6, merge: 500, seed: 4848, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_ScanReplaceSpecialAct2SewersPresets') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'waypoint')).toBe(true) }) test('Ancient Tunnels (level 50): places golden chest', () => { const pieces = createStandardPieceSet('Act 2 - Sewer', sewer2ExtraPieces) const res = generateMaze({ levelId: 50, levelName: 'Ancient Tunnels', sectionSize: 10, minRooms: 5, merge: 500, seed: 5050, pieces, }) expect(res.stats.specialPassesApplied).toContain('DRLGMAZE_ScanReplaceSpecialAct2SewersPresets') const specials = res.stats.specialsApplied as { room: number; kind: string; sides: string }[] expect(specials.some(s => s.kind === 'treasure')).toBe(true) }) }) /* ----------------------------------------------------------------------- * * Classification tests * ----------------------------------------------------------------------- */ describe('Piece Classification for Special Presets', () => { test('classifies waypoint pieces', () => { expect(classifyMazePieceName('Act 2 - Tomb Waypoint N', 'Act 2 - Tomb')).toEqual({ kind: 'waypoint', sides: 'N', }) expect(classifyMazePieceName('Act 5 - Temple NE Waypoint', 'Act 5 - Temple')).toEqual({ kind: 'waypoint', sides: 'NE', }) expect(classifyMazePieceName('Act 5 - Ice waypoint N', 'Act 5 - Ice Caves')).toEqual({ kind: 'waypoint', sides: 'N', }) }) test('classifies quest and special pieces', () => { expect(classifyMazePieceName('Act 2 - Tomb Talrasha N', 'Act 2 - Tomb')).toEqual({ kind: 'quest', sides: 'N', }) expect(classifyMazePieceName("Act 2 - Sewer Radament's Lair S", 'Act 2 - Sewer')).toEqual({ kind: 'quest', sides: 'S', }) expect(classifyMazePieceName('Act 2 - Lair Tight Spot S', 'Act 2 - Lair')).toEqual({ kind: 'quest', sides: 'S', }) expect(classifyMazePieceName('Act 1 - Barracks Forge W', 'Act 1 - Barracks')).toEqual({ kind: 'quest', sides: 'W', }) expect(classifyMazePieceName('Act 4 - Bridge 1', 'Act 4 - Lava')).toEqual({ kind: 'next', sides: '', }) expect(classifyMazePieceName('Act 3 - Mephisto Complex', 'Act 3 - Kurast')).toEqual({ kind: 'quest', sides: '', }) expect(classifyMazePieceName('Act 5 - Temple Final Room', 'Act 5 - Temple')).toEqual({ kind: 'quest', sides: '', }) expect(classifyMazePieceName('Act 5 - ThroneRoom Entrance', 'Act 5 - Baal')).toEqual({ kind: 'quest', sides: '', }) }) }) })